charlesv3 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of charlesv3 might be problematic. Click here for more details.
- package/README.md +35 -0
- package/charles.bat +3 -0
- package/configCharles.js +43 -0
- package/connectToChurch/averageFilter.js +22 -0
- package/connectToChurch/cookieHandler.js +43 -0
- package/connectToChurch/getAverage.js +121 -0
- package/connectToChurch/getBearer.js +55 -0
- package/connectToChurch/listToday.js +49 -0
- package/connectToChurch/sneakyChurch.js +132 -0
- package/connectToChurch/superParse.js +27 -0
- package/connectToFacebook/crazyJob.json +10 -0
- package/connectToFacebook/editPayload.js +52 -0
- package/connectToFacebook/kindMessages.json +36 -0
- package/connectToFacebook/prettyString.js +25 -0
- package/connectToFacebook/randomMessage.js +8 -0
- package/connectToFacebook/sneakyFacebook.js +215 -0
- package/createConfig.js +46 -0
- package/createPayload.js +54 -0
- package/getZone.js +103 -0
- package/index.js +123 -0
- package/lastRun.js +24 -0
- package/package.json +28 -0
- package/prettyStringZones.js +18 -0
- package/smlReport.js +143 -0
@@ -0,0 +1,215 @@
|
|
1
|
+
import { existsSync, promises } from "fs"
|
2
|
+
import puppeteer from "puppeteer-extra"
|
3
|
+
import StealthPlugin from 'puppeteer-extra-plugin-stealth'
|
4
|
+
import { getZone } from "../getZone.js"
|
5
|
+
import { prettyString } from "./prettyString.js"
|
6
|
+
import { editPayload } from "./editPayload.js"
|
7
|
+
import ora from "ora"
|
8
|
+
|
9
|
+
puppeteer.use(StealthPlugin())
|
10
|
+
|
11
|
+
|
12
|
+
const wiggy = async (page) => {
|
13
|
+
const viewport = await page.viewport()|| { width: 1280, height: 800 }
|
14
|
+
const maxX = await viewport.width
|
15
|
+
const maxY = await viewport.height
|
16
|
+
const x = Math.floor(Math.random() * maxX);
|
17
|
+
const y = Math.floor(Math.random() * maxY);
|
18
|
+
|
19
|
+
// Move the mouse with a smooth transition
|
20
|
+
await page.mouse.move(x, y, { steps: 10 });
|
21
|
+
}
|
22
|
+
|
23
|
+
|
24
|
+
|
25
|
+
const waitForE2ee = async (e2ee, page) => {
|
26
|
+
let pinBox = null
|
27
|
+
try {
|
28
|
+
pinBox = await page.waitForSelector('input[id="mw-numeric-code-input-prevent-composer-focus-steal"]', {
|
29
|
+
timeout: 5000
|
30
|
+
})
|
31
|
+
// eslint-disable-next-line no-unused-vars
|
32
|
+
} catch (err) {
|
33
|
+
await sleep(500)
|
34
|
+
}
|
35
|
+
if (pinBox) {
|
36
|
+
await pinBox.type(e2ee, { delay: 250 })
|
37
|
+
}
|
38
|
+
|
39
|
+
}
|
40
|
+
|
41
|
+
const goToChat = async (allTheChats, page, id) => {
|
42
|
+
if (page.url().includes(id)) {
|
43
|
+
return
|
44
|
+
}
|
45
|
+
let chatFound = false
|
46
|
+
|
47
|
+
for (const chat of allTheChats) {
|
48
|
+
if (chat.id === id.toString()) {
|
49
|
+
chatFound = true
|
50
|
+
await page.evaluate( async (href) => {
|
51
|
+
const element = document.querySelector(`a[href="${href}"]`);
|
52
|
+
if (element) {
|
53
|
+
element.scrollIntoView({ behavior: 'smooth', block: 'center' });
|
54
|
+
await element.click()
|
55
|
+
}
|
56
|
+
|
57
|
+
}, chat.href);
|
58
|
+
}
|
59
|
+
}
|
60
|
+
if (!chatFound) {
|
61
|
+
await page.goto(`https://www.messenger.com/t/${id}`)
|
62
|
+
await page.waitForNetworkIdle()
|
63
|
+
}
|
64
|
+
}
|
65
|
+
|
66
|
+
|
67
|
+
const login = async (page, mail, pass) => {
|
68
|
+
// logs into facebook messenger
|
69
|
+
await page.goto('https://messenger.com/', { waitUntil: 'networkidle2'})
|
70
|
+
|
71
|
+
// Get elements
|
72
|
+
const mailBox = await page.waitForSelector('input[id="email"]')
|
73
|
+
const passBox = await page.waitForSelector('input[id="pass"]')
|
74
|
+
const button = await page.waitForSelector('button[id="loginbutton"]')
|
75
|
+
|
76
|
+
// Send in data
|
77
|
+
await mailBox.type(mail, { delay: 250 })
|
78
|
+
await passBox.type(pass, { delay: 375 })
|
79
|
+
await button.click()
|
80
|
+
await page.waitForNavigation()
|
81
|
+
}
|
82
|
+
|
83
|
+
const saveCookies = async (browser) => {
|
84
|
+
// Goes and smuggles cookies
|
85
|
+
const cookies = await browser.cookies()
|
86
|
+
await promises.writeFile('resources/messenger.json', JSON.stringify(cookies, null, 4))
|
87
|
+
}
|
88
|
+
|
89
|
+
const loadCookies = async (browser, page) => {
|
90
|
+
await page.goto('https://messenger.com/')
|
91
|
+
let cookies
|
92
|
+
try {
|
93
|
+
cookies = await JSON.parse( await promises.readFile('resources/messenger.json'))
|
94
|
+
// eslint-disable-next-line no-unused-vars
|
95
|
+
} catch(e) {
|
96
|
+
return
|
97
|
+
}
|
98
|
+
|
99
|
+
for (const cookie of cookies) {
|
100
|
+
browser.setCookie(cookie)
|
101
|
+
}
|
102
|
+
|
103
|
+
await page.reload()
|
104
|
+
}
|
105
|
+
|
106
|
+
const sleep = (ms) => {
|
107
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
108
|
+
}
|
109
|
+
|
110
|
+
export const sneakyFacebook = async (testId=null, headless=true, e2ee) => {
|
111
|
+
let spool = ora('Booting up Charles').start()
|
112
|
+
// Set up environment (is that how it's spelt?)
|
113
|
+
const browser = await puppeteer.launch({
|
114
|
+
headless: headless, // toggle if you want to see the browser
|
115
|
+
args: [
|
116
|
+
'--disable-infobars',
|
117
|
+
'--start-maximized',
|
118
|
+
'--disable-extensions',
|
119
|
+
'--window-size=1920,1080',
|
120
|
+
'--disable-gpu',
|
121
|
+
'--user-agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36'
|
122
|
+
],
|
123
|
+
})
|
124
|
+
const page = await browser.newPage()
|
125
|
+
// FB user and password
|
126
|
+
spool.text = 'Sequestering your identification'
|
127
|
+
spool.color = 'blue'
|
128
|
+
const userObj = await JSON.parse( await promises.readFile('resources/config.json'))
|
129
|
+
const username = userObj.botname
|
130
|
+
const password = userObj.botpassword
|
131
|
+
|
132
|
+
|
133
|
+
// Try to snag cookies and skip login
|
134
|
+
await loadCookies(browser, page)
|
135
|
+
const mailBox = await page.waitForSelector('input[id="email"]', { timeout: 5000 }).catch(() => null);
|
136
|
+
if (mailBox) {
|
137
|
+
await login(page, username, password)
|
138
|
+
await saveCookies(browser)
|
139
|
+
}
|
140
|
+
spool.text = "I'm totally a human, Facebook, TRUST ME!"
|
141
|
+
spool.color = 'red'
|
142
|
+
|
143
|
+
|
144
|
+
|
145
|
+
|
146
|
+
await waitForE2ee(e2ee, page)
|
147
|
+
|
148
|
+
let zones
|
149
|
+
|
150
|
+
// Go through each chat and zone and do cool stuff
|
151
|
+
if (existsSync('resources/charlesConfig.json')) {
|
152
|
+
zones = await promises.readFile('resources/charlesConfig.json')
|
153
|
+
.then(JSON.parse)
|
154
|
+
} else {
|
155
|
+
zones = await getZone()
|
156
|
+
}
|
157
|
+
|
158
|
+
let reformattedPayload = await editPayload()
|
159
|
+
|
160
|
+
let intervalId = setInterval(() => wiggy(page), 5000);
|
161
|
+
spool.succeed(' Everything is spick and span')
|
162
|
+
// Go through list of zones and send a message to each
|
163
|
+
delete zones?.Dothan
|
164
|
+
|
165
|
+
for (const zone in zones) {
|
166
|
+
let waitingSpool = ora(`Storming the castle`).start()
|
167
|
+
|
168
|
+
const allTheChats = await page.$$eval(
|
169
|
+
'a[class="x1i10hfl x1qjc9v5 xjbqb8w xjqpnuy xa49m3k xqeqjp1 x2hbi6w x13fuv20 xu3j5b3 x1q0q8m5 x26u7qi x972fbf xcfux6l x1qhh985 xm0m39n x9f619 x1ypdohk xdl72j9 x2lah0s xe8uvvx x2lwn1j xeuugli xexx8yu x4uap5 x18d9i69 xkhd6sd x1n2onr6 x16tdsg8 x1hl2dhg xggy1nq x1ja2u2z x1t137rt x1q0g3np x87ps6o x1lku1pv x1a2a7pz x1lq5wgf xgqcy7u x30kzoy x9jhf4c xdj266r x11i5rnm xat24cr x1mh8g0r x78zum5"]',
|
170
|
+
(elements) => {
|
171
|
+
return elements.map((item) => {
|
172
|
+
const href = item.getAttribute('href');
|
173
|
+
return {
|
174
|
+
id: href.match(/(\d+)\/?$/)?.[1],
|
175
|
+
href: href
|
176
|
+
};
|
177
|
+
});
|
178
|
+
}
|
179
|
+
);
|
180
|
+
|
181
|
+
waitingSpool.text = `Sending a message to ${zone}`
|
182
|
+
waitingSpool.color = 'magenta'
|
183
|
+
|
184
|
+
const message = await prettyString(zone, reformattedPayload[zone], reformattedPayload["avg"])
|
185
|
+
// bar.start(message.length, 0)
|
186
|
+
|
187
|
+
// So originally the problem was not
|
188
|
+
// appearing to be human, that's why we have
|
189
|
+
// humantype and other things like that.
|
190
|
+
// We've discovered that you can just
|
191
|
+
// copy and paste and that's human
|
192
|
+
// enough. If you try to change this, remember
|
193
|
+
// that you have to be human! :)
|
194
|
+
|
195
|
+
await goToChat(allTheChats, page, testId ? testId : zones[zone])
|
196
|
+
|
197
|
+
await sleep(5000)
|
198
|
+
await page.evaluate((message) => {
|
199
|
+
navigator.clipboard.writeText(message);
|
200
|
+
}, message)
|
201
|
+
await page.focus('div[role="textbox"]')
|
202
|
+
// await humanType(page, boxy, message, bar)
|
203
|
+
await sleep(Math.floor(Math.random() * 500) + 1)
|
204
|
+
await page.keyboard.down('Control');
|
205
|
+
await page.keyboard.press('KeyV');
|
206
|
+
await page.keyboard.up('Control');
|
207
|
+
await sleep(Math.floor(Math.random() * 500) + 1)
|
208
|
+
const button = await page.waitForSelector('div[aria-label="Press enter to send"]')
|
209
|
+
await button.click()
|
210
|
+
await sleep(5000)
|
211
|
+
waitingSpool.succeed(`Message sent to ${zone}`)
|
212
|
+
}
|
213
|
+
clearInterval(intervalId)
|
214
|
+
await browser.close()
|
215
|
+
}
|
package/createConfig.js
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
import chalk from "chalk";
|
2
|
+
import { promises } from "fs";
|
3
|
+
import prompts from "prompts";
|
4
|
+
|
5
|
+
|
6
|
+
export async function createConfig(configPath) {
|
7
|
+
|
8
|
+
const questions = [
|
9
|
+
{
|
10
|
+
type: "text",
|
11
|
+
name: "username",
|
12
|
+
message: "What is your churchofjesuschrist.org username?",
|
13
|
+
},
|
14
|
+
{
|
15
|
+
type: "password",
|
16
|
+
name: "password",
|
17
|
+
message: "What is your password?",
|
18
|
+
},
|
19
|
+
{
|
20
|
+
type: "text",
|
21
|
+
name: "botname",
|
22
|
+
message: "What email is your bot registered under?",
|
23
|
+
},
|
24
|
+
{
|
25
|
+
type: "password",
|
26
|
+
name: "botpassword",
|
27
|
+
message: "What is the password for that Facebook account?",
|
28
|
+
},
|
29
|
+
{
|
30
|
+
type: "toggle",
|
31
|
+
name: "save",
|
32
|
+
message: "Would you like to save this information for future logins (recommended)?",
|
33
|
+
initial: true,
|
34
|
+
active: "yes",
|
35
|
+
inactive: "no",
|
36
|
+
},
|
37
|
+
];
|
38
|
+
console.log(chalk.dim("Setting up Charles"));
|
39
|
+
const response = await prompts(questions);
|
40
|
+
if (response.save) {
|
41
|
+
await promises.writeFile(configPath, JSON.stringify(response));
|
42
|
+
return response;
|
43
|
+
} else {
|
44
|
+
return response;
|
45
|
+
}
|
46
|
+
}
|
package/createPayload.js
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
import { existsSync, readFileSync, writeFileSync } from "fs";
|
2
|
+
import { configCharles } from './configCharles.js';
|
3
|
+
|
4
|
+
function isMoreThan12HoursOld(timestamp) {
|
5
|
+
const TWELVE_HOURS_MS = 12 * 60 * 60 * 1000;
|
6
|
+
return (Date.now() - timestamp) > TWELVE_HOURS_MS;
|
7
|
+
}
|
8
|
+
|
9
|
+
export async function createPayload(list, avgMessage) {
|
10
|
+
const FILE_NAME = 'payload.json';
|
11
|
+
|
12
|
+
if (existsSync(FILE_NAME)) {
|
13
|
+
try {
|
14
|
+
const oldpay = JSON.parse(readFileSync(FILE_NAME, 'utf-8'));
|
15
|
+
if (!isMoreThan12HoursOld(oldpay.stamp)) {
|
16
|
+
return oldpay;
|
17
|
+
}
|
18
|
+
} catch (error) {
|
19
|
+
console.error(`Error reading ${FILE_NAME}:`, error);
|
20
|
+
}
|
21
|
+
}
|
22
|
+
|
23
|
+
let zoneList;
|
24
|
+
try {
|
25
|
+
zoneList = await configCharles('./resources/charlesConfig.json')
|
26
|
+
} catch (error) {
|
27
|
+
console.error("Error fetching zone data:", error);
|
28
|
+
return null; // Return `null` or throw an error if needed
|
29
|
+
}
|
30
|
+
|
31
|
+
delete zoneList?.Dorthan
|
32
|
+
let payload = { stamp: Date.now(), average: avgMessage, payload: {} }
|
33
|
+
const zones = Object.keys(zoneList);
|
34
|
+
|
35
|
+
// If list.persons exists and is an object, use its values;
|
36
|
+
// if it's already an array, use it directly.
|
37
|
+
let cleaned = list
|
38
|
+
|
39
|
+
|
40
|
+
for (let zone of zones) {
|
41
|
+
payload.payload[zone] = cleaned.filter(person =>
|
42
|
+
person.zoneName?.trim().toLowerCase() === zone.toLowerCase()
|
43
|
+
)
|
44
|
+
}
|
45
|
+
|
46
|
+
try {
|
47
|
+
writeFileSync(FILE_NAME, JSON.stringify(payload, null, 2))
|
48
|
+
} catch (error) {
|
49
|
+
console.error(`Error writing ${FILE_NAME}:`, error)
|
50
|
+
}
|
51
|
+
|
52
|
+
return payload;
|
53
|
+
}
|
54
|
+
|
package/getZone.js
ADDED
@@ -0,0 +1,103 @@
|
|
1
|
+
import puppeteer from "puppeteer";
|
2
|
+
import { cookieHandler, saveCookies } from "./connectToChurch/cookieHandler.js";
|
3
|
+
import { getBearer } from "./connectToChurch/getBearer.js";
|
4
|
+
import { jwtDecode } from "jwt-decode";
|
5
|
+
import { promises as fs } from "node:fs";
|
6
|
+
import ora from "ora";
|
7
|
+
|
8
|
+
async function login(user, pass, page) {
|
9
|
+
// Enter username
|
10
|
+
await page.goto('https://referralmanager.churchofjesuschrist.org/')
|
11
|
+
await page.type("input[name='identifier']", user)
|
12
|
+
await page.click("input[type='submit']")
|
13
|
+
|
14
|
+
// Enter password
|
15
|
+
await page.waitForSelector("input[name='credentials.passcode']", {timeout: 10000})
|
16
|
+
await page.type("input[name='credentials.passcode']", pass)
|
17
|
+
await page.click("input[type='submit']")
|
18
|
+
await page.waitForNavigation()
|
19
|
+
// get cookies and save
|
20
|
+
const cookies = await page.cookies()
|
21
|
+
await saveCookies(cookies)
|
22
|
+
|
23
|
+
}
|
24
|
+
|
25
|
+
async function getPeopleList(page, bearer, decodedBearer) {
|
26
|
+
const list = await page.evaluate(async (decodedBearer, bearer) => {
|
27
|
+
const peopleList = await fetch(`https://referralmanager.churchofjesuschrist.org/services/people/mission/${JSON.stringify(decodedBearer.missionId)}?includeDroppedPersons=true`, {
|
28
|
+
method: 'GET',
|
29
|
+
headers: {
|
30
|
+
'Authorization' : `Bearer ${bearer}`
|
31
|
+
}
|
32
|
+
})
|
33
|
+
const list = await peopleList.text()
|
34
|
+
return list
|
35
|
+
}, decodedBearer, bearer)
|
36
|
+
return list
|
37
|
+
}
|
38
|
+
|
39
|
+
export async function getZone(config=null) {
|
40
|
+
const spinner = ora('Getting zone info').start()
|
41
|
+
const browser = await puppeteer.launch()
|
42
|
+
const page = await browser.newPage()
|
43
|
+
if (!config) {
|
44
|
+
config = await fs.readFile('./resources/config.json', 'utf8').then(JSON.parse);
|
45
|
+
}
|
46
|
+
const user = config.username
|
47
|
+
const pass = config.password
|
48
|
+
|
49
|
+
const needToGoOnline = async () => {
|
50
|
+
try {
|
51
|
+
let rawList = fs.readFile('./resources/rawList.json', 'utf-8').then(JSON.parse)
|
52
|
+
return rawList
|
53
|
+
// eslint-disable-next-line no-unused-vars
|
54
|
+
} catch (e) {
|
55
|
+
return false
|
56
|
+
}
|
57
|
+
}
|
58
|
+
|
59
|
+
let useWitchery = await needToGoOnline()
|
60
|
+
let stuff = undefined
|
61
|
+
|
62
|
+
if (!useWitchery) {
|
63
|
+
const okayToSkipLogin = await cookieHandler(page)
|
64
|
+
if (okayToSkipLogin) {
|
65
|
+
await page.goto('https://referralmanager.churchofjesuschrist.org/')
|
66
|
+
const isLoggedOut = await page.$("input[name='identifier']")
|
67
|
+
if (isLoggedOut) {
|
68
|
+
spinner.color = 'red'
|
69
|
+
spinner.text = 'Session expired, logging in again'
|
70
|
+
await login(user, pass, page)
|
71
|
+
}
|
72
|
+
} else {
|
73
|
+
await login(user, pass, page)
|
74
|
+
}
|
75
|
+
|
76
|
+
const bearer = await getBearer(page)
|
77
|
+
const decodedBearer = jwtDecode(bearer)
|
78
|
+
|
79
|
+
spinner.color = 'yellow'
|
80
|
+
spinner.text = 'Making it look pretty'
|
81
|
+
|
82
|
+
stuff = await JSON.parse(await getPeopleList(page, bearer, decodedBearer))
|
83
|
+
} else {
|
84
|
+
spinner.text = 'Skipping login'
|
85
|
+
spinner.color = 'green'
|
86
|
+
stuff = useWitchery
|
87
|
+
}
|
88
|
+
|
89
|
+
const list = stuff.persons
|
90
|
+
|
91
|
+
let zoneList = [];
|
92
|
+
for (let i = 0; i < list.length; i++) {
|
93
|
+
if (list[i].zoneName) {
|
94
|
+
const trimmedZoneName = list[i].zoneName.trim();
|
95
|
+
if (!zoneList.includes(trimmedZoneName)) {
|
96
|
+
zoneList.push(trimmedZoneName);
|
97
|
+
}
|
98
|
+
}
|
99
|
+
}
|
100
|
+
await browser.close()
|
101
|
+
spinner.succeed(' Zone information updated!')
|
102
|
+
return zoneList
|
103
|
+
}
|
package/index.js
ADDED
@@ -0,0 +1,123 @@
|
|
1
|
+
#!/usr/bin/env node
|
2
|
+
|
3
|
+
import chalk from "chalk";
|
4
|
+
import prompts from "prompts";
|
5
|
+
import { configCharles } from "./configCharles.js";
|
6
|
+
import { sneakyChurch } from "./connectToChurch/sneakyChurch.js";
|
7
|
+
import { createPayload } from "./createPayload.js";
|
8
|
+
import { promises as fs, existsSync, mkdirSync } from "fs";
|
9
|
+
import { sneakyFacebook } from "./connectToFacebook/sneakyFacebook.js";
|
10
|
+
import { lastRun } from "./lastRun.js";
|
11
|
+
import { smlReport } from "./smlReport.js";
|
12
|
+
import { createConfig } from "./createConfig.js";
|
13
|
+
|
14
|
+
async function main() {
|
15
|
+
if (!existsSync('./resources')) {
|
16
|
+
mkdirSync('./resources')
|
17
|
+
}
|
18
|
+
console.clear()
|
19
|
+
console.log(chalk.dim('Welcome to Charles, booting up...'))
|
20
|
+
let config
|
21
|
+
try {
|
22
|
+
config = await fs.readFile('./resources/config.json', 'utf8').then(JSON.parse);
|
23
|
+
// eslint-disable-next-line no-unused-vars
|
24
|
+
} catch (e) {
|
25
|
+
console.log(chalk.redBright("Looks like you haven't used Charles before! Let's set this up...\n"))
|
26
|
+
config = await createConfig("resources/config.json")
|
27
|
+
}
|
28
|
+
|
29
|
+
async function menu() {
|
30
|
+
const questions = {
|
31
|
+
type: 'select',
|
32
|
+
name: 'program',
|
33
|
+
message: 'What should we do today?',
|
34
|
+
choices: [
|
35
|
+
{ title: 'Send Charles message', value: 'charles'},
|
36
|
+
{ title: 'Get SML report', value: 'report'},
|
37
|
+
{title: 'Change settings', value: 'settings'},
|
38
|
+
{ title: 'Test run Charles', value: 'test'},
|
39
|
+
{ title: 'Yeet outta here', value: 'exit'},
|
40
|
+
],
|
41
|
+
initial: 0,
|
42
|
+
instructions: false
|
43
|
+
}
|
44
|
+
return await prompts(questions)
|
45
|
+
}
|
46
|
+
|
47
|
+
let select;
|
48
|
+
do {
|
49
|
+
select = await menu();
|
50
|
+
if (!select.program) {
|
51
|
+
console.log(chalk.red(chalk.italic("No option selected. Please try again.")));
|
52
|
+
continue;
|
53
|
+
}
|
54
|
+
if (select.program?.includes('charles')) {
|
55
|
+
// run charles
|
56
|
+
// Only run if it has been less than 24 hours...
|
57
|
+
const lessThan24HoursAgo = await lastRun()
|
58
|
+
const e2ee = await prompts({
|
59
|
+
type: 'text',
|
60
|
+
name: 'e2ee',
|
61
|
+
message: 'What is your e2ee pin?'
|
62
|
+
})
|
63
|
+
if (lessThan24HoursAgo) {
|
64
|
+
const moveOn = await prompts(
|
65
|
+
{
|
66
|
+
type: 'confirm',
|
67
|
+
name: 'value',
|
68
|
+
message: "Woah there, do you still wanna run Charles? It's been less than two days since he last ran.",
|
69
|
+
initial: true
|
70
|
+
}
|
71
|
+
)
|
72
|
+
if (moveOn.value) {
|
73
|
+
console.clear()
|
74
|
+
const [todaysList, beginPackage] = await sneakyChurch(config.username, config.password)
|
75
|
+
await createPayload(todaysList, beginPackage)
|
76
|
+
await sneakyFacebook(undefined, undefined, e2ee.e2ee)
|
77
|
+
}
|
78
|
+
} else {
|
79
|
+
console.clear()
|
80
|
+
const [todaysList, beginPackage] = await sneakyChurch(config.username, config.password)
|
81
|
+
await createPayload(todaysList, beginPackage)
|
82
|
+
await sneakyFacebook(undefined, undefined, e2ee)
|
83
|
+
}
|
84
|
+
} else if (select.program?.includes('report')) {
|
85
|
+
try {
|
86
|
+
const report = await smlReport()
|
87
|
+
console.log(report)
|
88
|
+
// eslint-disable-next-line no-unused-vars
|
89
|
+
} catch (e) {
|
90
|
+
console.log(chalk.redBright("Your cookies must've gone stale :( Try test running Charles."))
|
91
|
+
}
|
92
|
+
} else if (select.program?.includes('exit')) {
|
93
|
+
console.log("Exiting...");
|
94
|
+
break
|
95
|
+
} else if (select.program?.includes('settings')) {
|
96
|
+
try {
|
97
|
+
await fs.unlink('./resources/charlesConfig.json')
|
98
|
+
await configCharles('./resources/charlesConfig.json')
|
99
|
+
// eslint-disable-next-line no-unused-vars
|
100
|
+
} catch (err) {
|
101
|
+
await configCharles('./resources/charlesConfig.json')
|
102
|
+
}
|
103
|
+
}
|
104
|
+
else if (select.program?.includes('test')) {
|
105
|
+
console.clear()
|
106
|
+
const security = await prompts([{
|
107
|
+
type: 'text',
|
108
|
+
name: 'testZone',
|
109
|
+
message: 'What is the Messenger ID of your test chat?'
|
110
|
+
}, {
|
111
|
+
type: 'text',
|
112
|
+
name: 'e2ee',
|
113
|
+
message: 'What is your e2ee pin?'
|
114
|
+
}])
|
115
|
+
console.clear()
|
116
|
+
const [todaysList, beginPackage] = await sneakyChurch(config.username, config.password, "", false)
|
117
|
+
await createPayload(todaysList, beginPackage)
|
118
|
+
await sneakyFacebook(security.testZone, false, security.e2ee)
|
119
|
+
}
|
120
|
+
} while (!select.program?.includes('exit'));
|
121
|
+
}
|
122
|
+
|
123
|
+
main();
|
package/lastRun.js
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
// Returns true if you should move on, false if it has been less than two days
|
2
|
+
|
3
|
+
import { promises } from "fs";
|
4
|
+
|
5
|
+
export async function lastRun() {
|
6
|
+
let timeStampFile
|
7
|
+
try {
|
8
|
+
timeStampFile = (await promises.readFile('resources/lastRun.txt')).toString()
|
9
|
+
// eslint-disable-next-line no-unused-vars
|
10
|
+
} catch (e) {
|
11
|
+
timeStampFile = null
|
12
|
+
}
|
13
|
+
if (timeStampFile) {
|
14
|
+
if ((Date.now() - (2 * 24 * 60 * 60 * 1000)) <= parseInt(timeStampFile)) {
|
15
|
+
return true
|
16
|
+
} else {
|
17
|
+
await promises.writeFile('resources/lastRun.txt', Date.now().toString())
|
18
|
+
return false
|
19
|
+
}
|
20
|
+
} else {
|
21
|
+
await promises.writeFile('resources/lastRun.txt', Date.now().toString())
|
22
|
+
return false
|
23
|
+
}
|
24
|
+
}
|
package/package.json
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
{
|
2
|
+
"name": "charlesv3",
|
3
|
+
"version": "1.0.1",
|
4
|
+
"description": "a friendly chabot written in js for use in the SCCM",
|
5
|
+
"main": "index.js",
|
6
|
+
"type": "module",
|
7
|
+
"scripts": {
|
8
|
+
"test": "echo \"Error: no test specified\" && exit 1",
|
9
|
+
"start": "node index.js"
|
10
|
+
},
|
11
|
+
"author": "Jack Jones",
|
12
|
+
"bin": {
|
13
|
+
"charles": "./index.js"
|
14
|
+
},
|
15
|
+
"license": "ISC",
|
16
|
+
"dependencies": {
|
17
|
+
"bottleneck": "^2.19.5",
|
18
|
+
"chalk": "^5.4.1",
|
19
|
+
"cli-progress": "^3.12.0",
|
20
|
+
"jwt-decode": "^4.0.0",
|
21
|
+
"node-fetch": "^3.3.2",
|
22
|
+
"ora": "^8.2.0",
|
23
|
+
"prompts": "^2.4.2",
|
24
|
+
"puppeteer": "^24.2.1",
|
25
|
+
"puppeteer-extra": "^3.3.6",
|
26
|
+
"puppeteer-extra-plugin-stealth": "^2.11.2"
|
27
|
+
}
|
28
|
+
}
|
@@ -0,0 +1,18 @@
|
|
1
|
+
export function prettyStringZones(data, avg) {
|
2
|
+
let result = '';
|
3
|
+
result += avg
|
4
|
+
result += "\n"
|
5
|
+
// Loop through each zone in the data
|
6
|
+
for (const zone in data.payload) {
|
7
|
+
if (data.payload.hasOwnProperty(zone)) {
|
8
|
+
result += `${zone}\n`; // Add the zone name
|
9
|
+
// For each member under the zone, list their name
|
10
|
+
data.payload[zone].forEach(person => {
|
11
|
+
result += ` - ${person.name}\n`; // Add the person's name
|
12
|
+
});
|
13
|
+
result += '\n'; // Add an empty line between zones
|
14
|
+
}
|
15
|
+
}
|
16
|
+
|
17
|
+
return result;
|
18
|
+
}
|