forked from Watchful1/Sketchpad
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmodLogNotifier.py
280 lines (230 loc) · 5.97 KB
/
modLogNotifier.py
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
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
#!/usr/bin/python3
import praw
import os
import logging.handlers
import sys
import configparser
import signal
import time
import traceback
import sqlite3
from datetime import datetime
from datetime import timedelta
USER_AGENT = "Mod Log Notifier (by /u/Watchful1)"
LOOP_TIME = 3 * 60
DATABASE_NAME = "database.db"
LOG_LEVEL = logging.INFO
SUBREDDIT = "SubTestBot1"
INFRACTIONS_LIMIT = 3
DAYS_OLD_TO_CLEAR = 30
REMOVAL_REASON = "douchebaggary/slur"
USERNAME = ""
PASSWORD = ""
CLIENT_ID = ""
CLIENT_SECRET = ""
LOG_FOLDER_NAME = "logs"
if not os.path.exists(LOG_FOLDER_NAME):
os.makedirs(LOG_FOLDER_NAME)
LOG_FILENAME = LOG_FOLDER_NAME+"/"+"bot.log"
LOG_FILE_BACKUPCOUNT = 5
LOG_FILE_MAXSIZE = 1024 * 1024 * 16
log = logging.getLogger("bot")
log.setLevel(LOG_LEVEL)
log_formatter = logging.Formatter('%(asctime)s - %(levelname)s: %(message)s')
log_stderrHandler = logging.StreamHandler()
log_stderrHandler.setFormatter(log_formatter)
log.addHandler(log_stderrHandler)
if LOG_FILENAME is not None:
log_fileHandler = logging.handlers.RotatingFileHandler(LOG_FILENAME,
maxBytes=LOG_FILE_MAXSIZE,
backupCount=LOG_FILE_BACKUPCOUNT)
log_fileHandler.setFormatter(log_formatter)
log.addHandler(log_fileHandler)
def signal_handler(signal, frame):
log.info("Handling interupt")
dbConn.commit()
dbConn.close()
sys.exit(0)
signal.signal(signal.SIGINT, signal_handler)
dbConn = sqlite3.connect(DATABASE_NAME)
c = dbConn.cursor()
c.execute('''
CREATE TABLE IF NOT EXISTS logs (
ID INTEGER PRIMARY KEY AUTOINCREMENT,
logID VARCHAR(80) NOT NULL,
logCreated TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
username VARCHAR(80) NOT NULL,
UNIQUE (logID)
)
''')
c.execute('''
CREATE TABLE IF NOT EXISTS keystore (
ID INTEGER PRIMARY KEY AUTOINCREMENT,
KeyName VARCHAR(80) NOT NULL,
KeyValue VARCHAR(80) NOT NULL,
UNIQUE (KeyName)
)
''')
dbConn.commit()
def addLog(logId, username):
c = dbConn.cursor()
try:
c.execute('''
INSERT INTO logs
(logID, username)
VALUES (?, ?)
''', (logId, username.lower()))
except sqlite3.IntegrityError:
return False
dbConn.commit()
return True
def getLogsOverLimit():
c = dbConn.cursor()
results = []
for row in c.execute('''
SELECT username
FROM logs
GROUP BY username
HAVING count(*) >= ?
''', (INFRACTIONS_LIMIT,)):
results.append(row[0])
return results
def clearLogsForUser(username):
c = dbConn.cursor()
c.execute('''
DELETE FROM logs
WHERE username = ?
''', (username.lower(),))
dbConn.commit()
if c.rowcount >= 1:
return True
else:
return False
def clearOldLogs():
c = dbConn.cursor()
c.execute('''
DELETE FROM logs
WHERE logCreated < ?
''', ((datetime.utcnow() - timedelta(days=DAYS_OLD_TO_CLEAR)).strftime("%Y-%m-%d %H:%M:%S"),))
dbConn.commit()
return True
def setKey(key, value):
c = dbConn.cursor()
result = c.execute('''
SELECT KeyName
FROM keystore
WHERE KeyName = ?
''', (key,))
if result.fetchone():
try:
c.execute('''
UPDATE keystore
SET KeyValue = ?
WHERE KeyName = ?
''', (value, key))
except sqlite3.IntegrityError:
return False
else:
try:
c.execute('''
INSERT INTO keystore
(KeyName, KeyValue)
VALUES (?, ?)
''', (key, value))
except sqlite3.IntegrityError:
return False
dbConn.commit()
return True
def getValue(key):
c = dbConn.cursor()
result = c.execute('''
SELECT KeyValue
FROM keystore
WHERE KeyName = ?
''', (key,))
resultTuple = result.fetchone()
if not resultTuple:
return None
else:
return resultTuple[0]
def deleteKey(key):
c = dbConn.cursor()
c.execute('''
DELETE FROM keystore
WHERE KeyName = ?
''', (key,))
dbConn.commit()
if c.rowcount == 1:
return True
else:
return False
once = False
debug = False
user = None
prawIni = False
if len(sys.argv) >= 2:
user = sys.argv[1]
for arg in sys.argv:
if arg == 'once':
once = True
elif arg == 'debug':
debug = True
log.setLevel(logging.DEBUG)
elif arg == 'prawini':
prawIni = True
if prawIni:
if user is None:
log.error("No user specified, aborting")
sys.exit(0)
try:
r = praw.Reddit(
user
, user_agent=USER_AGENT)
except configparser.NoSectionError:
log.error(f"User {user} not in praw.ini, aborting")
sys.exit(0)
else:
r = praw.Reddit(
username=USERNAME
,password=PASSWORD
,client_id=CLIENT_ID
,client_secret=CLIENT_SECRET
,user_agent=USER_AGENT)
log.info("Logged into reddit as /u/{}".format(str(r.user.me())))
while True:
try:
startTime = time.perf_counter()
log.debug("Starting run")
parsedTo = getValue("parsed_to")
if parsedTo is None:
parsedTo = datetime.utcnow()
setKey("parsed_to", parsedTo.strftime("%Y-%m-%d %H:%M:%S"))
else:
parsedTo = datetime.strptime(parsedTo, "%Y-%m-%d %H:%M:%S")
newParsedTo = None
for modLog in r.subreddit(SUBREDDIT).mod.log(limit=None):
logDatetime = datetime.utcfromtimestamp(modLog.created_utc)
if logDatetime < parsedTo:
break
if newParsedTo is None:
newParsedTo = logDatetime + timedelta(seconds=1)
log.debug(f"Parsing modlog: {modLog.id}")
if modLog.action == 'removecomment' and modLog.details.lower() == REMOVAL_REASON.lower():
log.info(f"Found matching removal, comment by {modLog.target_author}")
addLog(modLog.id, modLog.target_author)
if newParsedTo is not None:
setKey("parsed_to", newParsedTo.strftime("%Y-%m-%d %H:%M:%S"))
for username in getLogsOverLimit():
log.info(f"User {username} over limit, sending message")
r.subreddit(SUBREDDIT).message(f"User hit {REMOVAL_REASON} limit",
f"User /u/{username} has hit {INFRACTIONS_LIMIT} removals in the "
f"last {DAYS_OLD_TO_CLEAR} days")
clearLogsForUser(username)
clearOldLogs()
log.debug("Run complete after: %d", int(time.perf_counter() - startTime))
except Exception as err:
log.warning("Hit an error in main loop")
log.warning(traceback.format_exc())
if once:
break
time.sleep(LOOP_TIME)