Skip to content

Instantly share code, notes, and snippets.

@luiseok
Last active September 15, 2022 16:19
Show Gist options
  • Save luiseok/c22c7a67c2311fa8d19c2268667df8e7 to your computer and use it in GitHub Desktop.
Save luiseok/c22c7a67c2311fa8d19c2268667df8e7 to your computer and use it in GitHub Desktop.
Scriptable-Dogdripnet-notification
// Variables used by Scriptable.
// These must be at the very top of the file. Do not edit.
// icon-color: deep-gray; icon-glyph: bone;
/**
* @author 어그로학교수
* released at 2022.09.15
* use as your own risk
*/
const CONSTANTS = {
LOG_LEVEL: 'debug',
USER_AGENT: 'Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/105.0.5195.100 Mobile/15E148 Safari/604.1',
BASE_DOGDRIP_HOST: 'https://dogdrip.net',
ALERT_CHOICES: {
CANCEL: -1,
GO_SETTINGS: 0,
SET_COOKIES: 1
}
}
const unreadCount = await main()
if (config.runsInWidget) {
let widget = dogdripWidget(unreadCount)
Script.setWidget(widget)
widget.presentSmall()
}
Script.complete()
function getRandomInt(min, max) {
min = Math.ceil(min);
max = Math.floor(max);
return Math.floor(Math.random() * (max - min + 1)) + min;
}
function dogdripWidget(count) {
let w = new ListWidget()
w.setPadding(10, 10, 10, 10)
let nextRefresh = Date.now() + 1000 * getRandomInt(60 * 5, 60 * 5 * 5) // Tries between 5 mins and 25mins
w.refreshAfterDate = new Date(nextRefresh)
let myGradient = new LinearGradient()
w.backgroundColor = new Color("#2e4361")
myGradient.colors = [new Color('#44444466'), new Color('#88888855'), new Color("#66666655")]
myGradient.locations = [0, 0.8, 1]
w.backgroundGradient = myGradient
let title = w.addText("Dogdrip")
title.textColor = Color.white()
title.font = Font.largeTitle()
if (count >= 0) {
let content = w.addText(`${count}개의 읽지않은 알림이 있습니다.`)
content.textColor = Color.white()
content.font = Font.headline()
} else {
let warning = w.addText(`아직 로그인 하지 않았습니다.`)
warning.textColor = Color.red()
warning.font = Font.headline()
let guide = w.addText('여기를 눌러 로그인 해주세요')
guide.textColor = Color.white()
guide.font = Font.regularSystemFont(10)
}
return w
}
function log(msg) {
if (config.runsInApp) {
console.log(msg);
}
let oldData = ''
let logManager = FileManager.iCloud()
const logPath = logManager.joinPath(logManager.documentsDirectory(), `${Script.name()}.txt`)
if (logManager.fileExists(logPath)) {
oldData = logManager.readString(logPath) + "\n"
}
let time = new Date()
oldData += `[${time.toISOString()}] ${Script.name()} : ${msg}`
logManager.writeString(logPath, oldData)
return time.toLocaleTimeString()
}
function sendNotification({
title,
from,
comment,
link,
subtitle,
groupId
}) {
let n = new Notification()
n.title = title
n.subtitle = subtitle
n.body = `[${from}] ${comment}`
n.openURL = link
if (groupId) n.threadIdentifier = groupId
n.schedule()
}
/*
Cookie Handling
*/
function resetCookie() {
Keychain.set(`${Script.name().toLowerCase()}-cookie`, "")
}
function setCookie(response) {
const next = response.cookies?.filter(({
name
}) => name.includes('rx_') || name.includes('PHPSESSID'))
.map(({
name,
value
}) => `${name}=${value}`)?.join("; ") ?? '';
log(next)
if (CONSTANTS.LOG_LEVEL === 'info') console.log(`setting new cookie with ${next}`)
if (next) {
Keychain.set(`${Script.name().toLowerCase()}-cookie`, next)
}
}
function getCookie() {
return Keychain.contains(`${Script.name().toLowerCase()}-cookie`) ? Keychain.get(`${Script.name().toLowerCase()}-cookie`) : ""
}
/**
* Dogdrip
*/
async function getLatestNotification() {
log(`Attempting getLatestNotification`)
if (CONSTANTS.LOG_LEVEL === 'info') log(`cookie: ${getCookie()}`)
const pageReq = new Request("https://www.dogdrip.net/index.php?mid=intro&act=dispNcenterliteNotifyList");
pageReq.headers = {
'Cookie': getCookie(),
'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 16_0 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) CriOS/105.0.5195.100 Mobile/15E148 Safari/604.1'
}
// Slower way, but reliable.
const wv = new WebView();
await wv.loadRequest(pageReq);
// Faster way, but not reliable.
// const result = await pageReq.loadString();
// await wv.loadHTML(result);
try {
const isloggedOut = await wv.evaluateJavaScript(`document.body.contains(document.getElementById("access"))`)
log(`isLogout: ${isloggedOut}`)
if (isloggedOut) {
const msg = '쿠키가 올바르지 않거나 로그인이 해제되었습니다. 다시 쿠키를 설정해주세요.';
if (config.runsInApp) {
const alert = new Alert();
alert.title = '오류 발생';
alert.message = msg
alert.addCancelAction("종료");
const canceled = await alert.presentAlert() === -1;
if (canceled) resetCookie();
Script.complete();
return [
[], 0
];
}
sendNotification({
title: "개드립 새 댓글 알림",
subtitle: "오류 발생",
from: "시스템",
link: "https://www.dogdrip.net/computer/432206629#user_content_help-how-to",
comment: msg
})
Script.complete();
return [
[], 0
];
}
// Server does not response well as you think.
// this can broke authenticated user
// else {
// setCookie(pageReq.response);
// }
} catch (error) {
log(error)
}
try {
const getNotiData = await wv.evaluateJavaScript(`
const href = Array.from(document.querySelectorAll("tbody > tr td a")??[]).map(a=>a.href);
const writer = Array.from(document.querySelectorAll("tbody > tr td a strong:first-of-type")??[]).map(w=>w.innerText);
const comment = Array.from(document.querySelectorAll("tbody > tr td a strong:last-of-type")??[]).map(c=>c.innerText);
const time = Array.from(document.querySelectorAll("tbody > tr td:last-of-type")??[]).map(nt=>(new Date(nt.innerText.replaceAll('\\n', ''))));
const result = href.map((link, index) => {
const fullUrl = new URL(link);
const commentId = fullUrl.searchParams.get("comment_srl") ?? null;
return {
href: link,
writer: writer[index],
comment: comment[index],
time: time[index],
commentId
}
}).sort((a, b) => a.time - b.time);
result;
`);
const getNotiCount = wv.evaluateJavaScript(`document.querySelector(".eq.badge.badge-primary.count")?.innerText`);
return Promise.all([getNotiData, getNotiCount])
} catch (error) {
log(error)
}
}
/**
* Send Notification
*/
function handleNewComments(comments = []) {
const now = new Date()
const uid = UUID.string()
for (const comment of comments) {
const createdAt = new Date(comment.time)
const timeDiff = Math.floor((now.getTime() - createdAt.getTime()) / 1000)
sendNotification({
title: '개드립 새 댓글 알림',
from: comment.writer,
subtitle: `${timeDiff}초 전`,
comment: comment.comment,
link: comment.href,
groupId: comments.length > 1 ? uid : null
})
}
}
async function checkCookieExists() {
if (config.runsInApp && !getCookie()) {
const alert = new Alert();
alert.title = '초기 설정 필요';
alert.message = '개드립 글을 참고하여 쿠키값을 세팅해주세요.';
alert.addAction("설정 방법 보기");
alert.addAction("쿠키값 붙여넣기");
alert.addCancelAction("종료");
const select = await alert.presentAlert();
switch (select) {
case CONSTANTS.ALERT_CHOICES.GO_SETTINGS: {
await Safari.openInApp("https://www.dogdrip.net/computer/432206629#user_content_help-how-to");
break;
}
case CONSTANTS.ALERT_CHOICES.SET_COOKIES: {
const prompt = new Alert();
prompt.title = '쿠키값 설정';
prompt.message = '가이드를 통해 확인하신 쿠키 String을 붙여넣으세요';
prompt.addSecureTextField("rx_autologin=....;rx_sesskey1=....;rx_sesskey2=....;", "");
prompt.addAction("확인");
prompt.addCancelAction("취소");
await prompt.presentAlert();
const cookieInput = prompt.textFieldValue(0);
const VALID_COOKIE_KEYS = ['rx_autologin', 'rx_sesskey1', 'rx_sesskey2'];
const IS_VALID = VALID_COOKIE_KEYS.filter(k => cookieInput.includes(k)).length === VALID_COOKIE_KEYS.length;
if (!cookieInput || !IS_VALID) {
const a = new Alert();
a.title = '오류';
a.message = IS_VALID ? '쿠키가 입력되지 않았습니다. 다시 확인해주세요' : '"로그인 상태 유지" 옵션이 포함되지 않은 쿠키입니다. 로그인 상태 유지 옵션을 눌러 로그인 한 후, 다시 시도해주세요.';
a.addCancelAction('닫기')
await a.presentAlert();
break;
}
Keychain.set(`${Script.name().toLowerCase()}-cookie`, prompt.textFieldValue(0))
}
case CONSTANTS.ALERT_CHOICES.CANCEL: {
break;
}
}
if (CONSTANTS.LOG_LEVEL === 'debug') log('not logged in!')
return false;
}
return true
}
async function main() {
const loggedIn = await checkCookieExists();
if (!loggedIn) {
return -1
}
try {
const [notificationData, unreadCount] = await getLatestNotification();
if (Array.isArray(notificationData) && notificationData.length > 0) {
let oldData = '';
let newComments = [];
const fileManager = FileManager.iCloud();
const dbFile = fileManager.joinPath(fileManager.documentsDirectory(), `dogdripCommentDB.txt`);
if (fileManager.fileExists(dbFile)) {
await fileManager.downloadFileFromiCloud(dbFile)
let commentHistory = fileManager.readString(dbFile).split(',').filter(s => s);
newComments = notificationData.filter(n => {
return !commentHistory.includes(n.commentId)
})
oldData = commentHistory.join(",")
if (newComments.length > 0) {
fileManager.writeString(dbFile, `${oldData},${newComments.map(n=>n.commentId).join(",")}`)
}
} else {
newComments = notificationData
fileManager.writeString(dbFile, newComments.map(n => n.commentId).join(","))
}
log(`Found ${newComments.length}`)
log(`UnreadCount: ${unreadCount ?? 0}`)
handleNewComments(newComments)
return unreadCount ?? 0
}
} catch (error) {
log(error)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment