-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathbot.js
197 lines (160 loc) · 5.72 KB
/
bot.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
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
const { execSync } = require('child_process');
const IRC = require('irc-framework');
const fs = require('fs');
const dns = require('dns').promises;
const CACHE_FILE = 'ip_cache.json';
// Load configuration
let config = JSON.parse(fs.readFileSync('config.json', 'utf8')); // Change from const to let
let reloadTimeout;
fs.watch('config.json', (eventType) => {
if (eventType === 'change') {
clearTimeout(reloadTimeout); // Clear any existing timeout
reloadTimeout = setTimeout(() => {
try {
console.log('Detected changes in config.json. Reloading configuration...');
config = JSON.parse(fs.readFileSync('config.json', 'utf8')); // Safely reload config
console.log('Configuration reloaded successfully.');
} catch (error) {
console.error('Error reloading config.json:', error);
}
}, 100); // Wait 100ms to ensure the file is fully written
}
});
// Function to perform a git pull and check for updates
function checkForUpdatesAndRestart() {
try {
console.log('Checking for updates...');
const output = execSync('git pull', { encoding: 'utf8' });
console.log(output);
if (!output.includes('Already up to date')) {
console.log('Updates detected, restarting the bot...');
process.exit(0); // Exit the process; assume a restart mechanism is in place
} else {
console.log('No updates found, continuing.');
}
} catch (error) {
console.error('Error checking for updates:', error);
}
}
// Call the function at the start
if (config.autoUpdate) {
checkForUpdatesAndRestart();
}
// Load cache file or initialize an empty cache
let cache = {};
if (fs.existsSync(CACHE_FILE)) {
cache = JSON.parse(fs.readFileSync(CACHE_FILE, 'utf8'));
}
const client = new IRC.Client();
client.connect({
host: config.server,
port: config.port,
nick: config.nickname,
username: config.username,
realname: config.realname,
password: config.password,
auto_reconnect: true,
});
// Save cache to file
function saveCache() {
fs.writeFileSync(CACHE_FILE, JSON.stringify(cache, null, 2), 'utf8');
}
// Helper function to resolve hostname to IPv4
async function resolveToIP(hostname) {
try {
const addresses = await dns.resolve(hostname); // Resolves both IPv4 and IPv6
return addresses[0]; // Use the first resolved IP
} catch (error) {
console.error(`Failed to resolve ${hostname} to an IP address:`, error);
return null;
}
}
// Helper function to query the VPN API
async function queryVpnApi(ipv4Address) {
const fetch = (await import('node-fetch')).default;
const url = `https://vpnapi.io/api/${ipv4Address}?key=${config.apiKey}`;
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`Failed to fetch data: ${response.statusText}`);
}
const data = await response.json();
cache[ipv4Address] = data.security;
saveCache();
return data.security;
} catch (error) {
console.error(`Error querying VPN API for ${ipv4Address}:`, error);
return null;
}
}
client.on('registered', () => {
console.log(`Connected to ${config.server} as ${config.nickname}`);
});
client.on('join', async (event) => {
if (event.nick === config.nickname) {
return;
}
// Check if the user is in the exemptUsers list (case insensitive)
if (config.exemptUsers.some(user => user.toLowerCase() === event.nick.toLowerCase())) {
console.log(`User ${event.nick} is exempt from checks.`);
return;
}
const userHost = event.hostname;
if (!userHost) {
console.log(`No hostname information for user ${event.nick}, aborting process.`);
return;
}
let ipAddress = userHost;
// If it's a hostname, resolve to an IP address (IPv4 or IPv6)
if (isNaN(parseInt(userHost.replace(/[\.:]/g, '')))) {
ipAddress = await resolveToIP(userHost);
if (!ipAddress) {
console.log(`No valid IP address found for ${userHost}, aborting process.`);
return;
}
}
console.log(`User ${event.nick} joined with IP address: ${ipAddress}`);
// Check if IP is in cache
let securityInfo = cache[ipAddress];
if (!securityInfo) {
// IP is not in cache, query the API
securityInfo = await queryVpnApi(ipAddress);
} else {
console.log(`Using cached security information for ${ipAddress}`);
}
if (securityInfo) {
console.log(`Security information for ${ipAddress}:`, securityInfo);
if (securityInfo.vpn || securityInfo.proxy || securityInfo.tor || securityInfo.relay) {
client.say('ChanServ', `QUIET ${event.channel} ${event.nick}`);
// Send a private message to each user in the notifyUsers array
config.notifyUsers.forEach(user => {
client.say(
user,
`User ${event.nick} joined with IP ${ipAddress} and triggered a security flag: VPN=${securityInfo.vpn}, Proxy=${securityInfo.proxy}, Tor=${securityInfo.tor}, Relay=${securityInfo.relay}`
);
});
}
} else {
console.log(`No security information available for ${ipAddress}`);
}
});
client.on('message', (event) => {
console.log(`Message from ${event.nick}: ${event.message}`);
// Respond to a specific command
if (event.message === '!hello') {
client.say(event.target, `Hello, ${event.nick}!`);
} else if (event.message.trim() === '!exempted') {
// Add a zero-width space in the middle of each username
const exemptList = config.exemptUsers
.map(user => {
const middle = Math.floor(user.length / 2);
return `${user.slice(0, middle)}\u200B${user.slice(middle)}`;
})
.join(', ');
// Send the list as a message to the channel or directly to the user
client.say(event.target, `Exempted users: ${exemptList || 'None'}`);
}
});
client.on('error', (error) => {
console.error('Error:', error);
});