-
Notifications
You must be signed in to change notification settings - Fork 4
/
find.js
142 lines (134 loc) · 4.95 KB
/
find.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
const Twitter = require('twitter');
const config = require('./credentials.js');
const client = new Twitter(config);
const prompt = require('prompt-sync')({sigint: true})
const imageCompare = require('./image-compare')
let username, extraQueries, report, callbackOnFinish, isAWS
async function start(_username, _queries, _isAWS, _report, _callback) {
username = _username
isAWS = _isAWS
report = _report
callbackOnFinish = _callback
extraQueries = _queries || []
// First find the original account.
const params = {
screen_name: username
}
const userObj = await new Promise((resolve, reject) => {
client.get('users/lookup.json', params, (err, data, response) => {
if (err) {
console.dir(err);
reject(err)
return;
}
const userObj = JSON.parse(response.body)[0]
resolve(userObj)
})
})
// Save the original user's profile picture.
await imageCompare.fetchAndSaveOriginalProfilePic(userObj.profile_image_url_https)
// Now find imposters.
const results = await findImposters(userObj, callbackOnFinish)
return results
}
// Determines whether the provider imposter is likely an imposter of the provided original account.
async function isImposter(imposter, original) {
if (imposter.screen_name === original.screen_name) {
// This is the original account, so not an imposter.
return false
}
if (await imageCompare.hasSameProfilePic(imposter)) {
// If the profile pictures match, then conclude its an imposter.
return true
}
// Otherwise, see if the bio matches.
// We look at the account bios. If greater than 50% of the words overlap, then we conclude it's
// likely an imposter.
const originalBioWords = original.description.split(' ')
const imposterBioWords = imposter.description.split(' ')
let matchedWordCount = 0
for (const word of originalBioWords) {
if (imposterBioWords.includes(word)) {
matchedWordCount++
}
}
if (matchedWordCount * 1.0 / originalBioWords.length > 0.5) {
return true
}
return false
}
async function findPossibleImpostersBySearchQuery(query) {
const params = {
q: query
}
return new Promise((resolve, reject) => {
client.get('users/search.json', params, async (err, data, response) => {
if (err) {
console.dir(err)
reject(err)
return;
}
const possibleImposters = JSON.parse(response.body)
resolve(possibleImposters)
})
})
}
async function findImposters(userObj) {
// Search for the user's current twitter name
const possibleImposters = await findPossibleImpostersBySearchQuery(userObj.name)
extraQueries.push(userObj.screen_name)
// If additional queries were supplied, search for these one too.
for (const extraQuery of extraQueries) {
const morePossibleImposters = await findPossibleImpostersBySearchQuery(extraQuery)
possibleImposters.push(...morePossibleImposters)
}
const imposters = []
for (const possibleImposter of possibleImposters) {
if (await isImposter(possibleImposter, userObj)) {
imposters.push(possibleImposter)
}
}
const imposterUsernames = imposters.map(it => it.screen_name)
const uniqueImposerUsernames = Array.from(new Set(imposterUsernames))
return await processImposters(uniqueImposerUsernames)
}
async function maybeReportImposters(names) {
if (isAWS) {
if (!report) {
return `Found imposters: ${names.join(', ')}`
}
} else {
prompt('\nProceed to report imposters?\n\n(press any key to continue, or CTRL-C to exit)\n')
}
const reportResults = {}
for (const name of names) {
const params = {
screen_name: name
}
reportResults[name] = await new Promise((resolve, reject) => {
client.post('users/report_spam.json', params, (err, data, response) => {
if (err) {
console.dir(err);
resolve('Error reporting this user (likely rate-limited). Try again later.')
return;
}
console.log(`Reported ${name}`)
resolve('Reported')
})
})
}
return `Found and reported imposters: ${Object.keys(reportResults).join(', ')}`
}
async function processImposters(imposters) {
console.log(`\nFound ${imposters.length} imposter accounts\n`)
if (imposters.length == 0) return "Found 0 imposter accounts"
for (const name of imposters) {
console.dir(name)
}
return maybeReportImposters(imposters)
}
module.exports = { start, isAWS, callbackOnFinish, username, report }
// Used if running as a local script.
if (require.main === module) {
start(process.argv[2], process.argv.length > 3 ? JSON.parse(process.argv[3]) : null)
}