/
troj_stena.py
293 lines (254 loc) · 12.4 KB
/
troj_stena.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
281
282
283
284
285
286
287
288
289
290
291
292
293
import asyncio
import logging
import os
import time
import collections
import datetime as datetime_whole
import core
import strings as st
import helpers as hp
import database as db
import messages
from datetime import datetime
from dotenv import load_dotenv
import globals
# Cogs
from commands.main import Main
# Start loading timer
start_time = time.time()
# Load enviroment variables
load_dotenv()
# Import project constants module
if globals.TESTVERSION:
import testconstants as cn
else:
import constants as cn
globals.bot.remove_command("help") # delete existing help command
# Logging
log_level = logging.INFO if not cn.DEBUG_MODE else logging.DEBUG
log_format = '%(asctime)-15s %(name)s:[%(levelname)s] %(message)s'
logging.basicConfig(level=log_level, format=log_format)
command_log = logging.getLogger('commands')
event_log = logging.getLogger('events')
management_log = logging.getLogger('management')
web_log = logging.getLogger('web')
# First event after bot launches
@globals.bot.event
async def on_ready():
logging.info(f"Bot loaded as {globals.bot.user.name}#{str(globals.bot.user.discriminator)}")
# Set global variables / setup bot + server
await globals.setup()
c = globals.bot.cogs
for cog in c:
globals.bot.remove_cog(cog)
# Load message data [Currently -> rules and faq] from database
msgs = {"rules", "faq"}
# region Message loading
for msg in msgs:
if db.check_document(cn.FB_MSGS, msg):
result = db.get_document(cn.FB_MSGS, msg).to_dict()["list"]
globals.message_data[msg] = collections.OrderedDict(sorted(result.items()))
loader = hp.load_default_data("faq" not in globals.message_data.keys(), "rules" not in globals.message_data.keys())
if len(loader) == 0:
logging.info("Loaded message database!")
else:
for msg in loader:
globals.message_data[msg] = db.get_document(cn.FB_MSGS, msg).to_dict()["list"]
logging.info("Loaded and uploaded default message database!")
# endregion
# Add existing users to database and load already saved
member_debug = {"l": 0, "c": 0}
# region User loading
for member in globals.server.members:
if member.id != globals.bot.user.id:
if db.check_document(cn.FB_USERS, str(member.id)):
try:
globals.users[member.id] = hp.User.from_dict(db.get_document(cn.FB_USERS, str(member.id)).to_dict())
member_debug["l"] += 1
except Exception:
management_log.warning("Synced userdata was not compatible! Overwriting.", exc_info=True)
member_debug["c"] += 1
db.load(cn.FB_USERS, str(member.id), vars(hp.User({"number": 0, "reasons": []}, [])))
else:
member_debug["c"] += 1
db.load(cn.FB_USERS, str(member.id), vars(hp.User({"number": 0, "reasons": []}, [])))
logging.info(f"Loaded member database!\n - Loaded:{member_debug['l']} +:{member_debug['c']}")
# endregion
# Load saved info about seminars from database
sem_list = ["kms", "fks", "ksp", "ufo", "prask"]
# region Seminar loading
for sem in sem_list:
# Load from db or create
if db.check_document(cn.FB_SEMINARS, sem):
try:
seminar = core.Seminar.from_dict(db.get_document(cn.FB_SEMINARS, sem).to_dict())
except Exception:
err = "Could not load existing seminars from database. Data might be missing or corrupted."
management_log.error(err)
seminar = core.Seminar(sem)
db.load(cn.FB_SEMINARS, sem, seminar.to_dict())
else:
seminar = core.Seminar(sem)
db.load(cn.FB_SEMINARS, sem, seminar.to_dict())
# First syncing with web will ocur in permaloop
globals.seminars.append(seminar)
globals.roles_and_emojis.append((seminar.role, seminar.emoji))
logging.info("Loaded seminar database!")
# endregion
# Mark bot's satate as ready after loading
globals.ready = True
for s in globals.seminars:
for r in s.rounds.values():
if r.msg is not None:
await r.check_round_message()
# Managment MSGS #
await messages.welcome_message()
await messages.role_message()
await messages.color_message()
# Initialize cogs #
Main()
# Enter permaloop
globals.bot.loop.create_task(permaloop())
# FINISH LOADING
logging.info(f"DONE! - Bot loaded in {round(time.time() - start_time, 2)}s")
print("-=[=================================================================]=-")
# ##############################
# ###### EVENT HANDLING #######
# ##############################
# used for uncached message_data :
# region uncached data events
@globals.bot.event
async def on_raw_reaction_add(payload):
user = globals.server.get_member(payload.user_id)
if payload.channel_id == cn.WELCOME_CHANNEL and not user.bot:
if globals.role_msg and globals.role_msg.id == payload.message_id:
for role, emoji in globals.roles_and_emojis:
if payload.emoji == emoji:
event_log.info(f"User {user.name}#{user.id} added reaction on {role.name}")
await user.add_roles(role)
elif globals.color_msg and globals.color_msg.id == payload.message_id:
if all(color_role not in user.roles for color_role in [x for x, _ in globals.colors]):
for role, emoji in globals.colors:
if payload.emoji.name == emoji:
event_log.info(f"User {user.name}#{user.id} added reaction on {role.name}")
await user.add_roles(role)
@globals.bot.event
async def on_raw_reaction_remove(payload):
user = globals.server.get_member(payload.user_id)
if payload.channel_id == cn.WELCOME_CHANNEL:
if globals.role_msg and globals.role_msg.id == payload.message_id:
for role, emoji in globals.roles_and_emojis:
if payload.emoji == emoji and (role in user.roles):
event_log.info(f"User {user.name}#{user.id} removed reaction on {role.name}")
await user.remove_roles(role)
elif globals.color_msg and globals.color_msg.id == payload.message_id:
for role, emoji in globals.colors:
if payload.emoji.name == emoji:
if role in user.roles:
event_log.info(f"User {user.name}#{user.id} removed reaction on {role.name}")
await user.remove_roles(role)
# endregion
# used for cached message_data :
# region cached data events
# events moderation commands wip
async def react_iter(look, iterator):
async for user in iterator():
if user == look:
return True
return False
async def add_warning(user, reason):
if user.id not in globals.users:
hp.create_user(str(user.id))
else:
globals.users[user.id].warnings["number"] += 1
globals.users[user.id].warnings["reasons"].append(reason)
event_log.info(f"{user.name} got warning because of {reason}."
f"They have {str(globals.users[user.id].warnings['number'])} warnings.")
if globals.users[user.id].warnings["number"] >= cn.WARNINGS_TO_BAN:
try:
await server.ban(user, reason=", ".join(globals.users[user.id].warnings["reasons"]), delete_message_days=0)
await user.dm_channel.send(st.BAN_MSG.format(cn.WARNINGS_TO_BAN))
except Exception:
await user.dm_channel.send(st.BAN_ERROR_U)
await server.get_channel(cn.ADMIN_CHANNEL).send(st.BAN_ERROR_A.format(user.name))
event_log.exception("Error while issuing ban", exc_info=True)
else:
await user.dm_channel.send(st.WARNING_MSG.format(str(globals.users[user.id].warnings["number"]),
str(cn.WARNINGS_TO_BAN)))
@globals.bot.event
async def on_reaction_add(react, _):
global server
global weird_messages
if react.message.channel == cn.TASKS_CHANNEL and react.emoji == cn.CHECKMARK_EMOJI and react.message.pinned:
comm = f"{globals.bot.get_prefix(react.message)}new"
if react.message.content.startswith(comm) and await react_iter(react.message.author, react.users):
task_name = react.message.content[5:]
await react.message.channel.send(st.TASK_COMPLETED.format(react.message.author.name, task_name))
await react.message.unpin()
elif react.emoji == globals.bot.get_emoji(cn.CHEATALERT_EMOJI): # cheat alert
if react.message.channel == server.get_channel(cn.MODERATING_CHANNEL): # moderating channel
nafetch = weird_messages[react.message.id]
react.message = await server.get_channel(nafetch[1]).fetch_message(nafetch[0])
if react.message.author.dm_channel is None:
await react.message.author.create_dm()
await react.message.author.dm_channel.send(st.DELETE_NOTICE.format(react.message.author.mention))
hour = react.message.created_at.time().hour + 2
minute = react.message.created_at.time().minute
second = react.message.created_at.time().second
sent = datetime_whole.time(hour=hour, minute=minute, second=second)
# react.message.created_at.time().hour += 2
timestr = sent.isoformat(timespec="seconds")
r_chan = react.message.channel.name
r_msg = react.message.content
details = st.DELETE_DETAILS.format(timestr, st.UTC_STRING, r_chan, r_msg)
await react.message.author.dm_channel.send(details)
# elif user in server.get_role(598517418968743957).members and react.message.channel
# != server.get_channel(599249382038044703): #role: Veducko, channel: isnt #moderating
# await react.message.channel.send(react.message.author +
# "!!! Your message was deleted because of serious rules violation.")
await react.message.delete()
await add_warning(react.message.author, "CHEAT alert emoji")
elif react.emoji == globals.bot.get_emoji(cn.QUESTIONABLE_EMOJI):
chan = server.get_channel(cn.MODERATING_CHANNEL)
s_message = st.SUSPICIOUS_MESSAGES(react.message.channel.name, react.message.content, react.message.jump_url)
newmsg = await chan.send(s_message)
weird_messages[newmsg.id] = (react.message.id, react.message.channel.id)
if not globals.ready or react.message.author == globals.bot:
return
# slowmode ????
"""
slowmode = 10
if message.author in server.get_role(id_bank["timeOut role"]).members: #timeOut role
if message.author.name not in timeouts:
timeouts[message.author.name] = time.time()
elif timeouts[message.author.name] + slowmode > time.time():
await message.delete()
return
else:
timeouts[message.author.name] = time.time()
"""
# endregion
# ####### MAIN LOOP ####### #
async def permaloop():
event_log.info("Entering permaloop ...")
await globals.bot.wait_until_ready()
while not globals.bot.is_closed():
checkpoint = time.time()
modification = False
for s in globals.seminars:
modification = await s.make_request("ulohy") # check tasks
for rnd in s.rounds.values():
modification = await s.make_request("vysledky", rnd.id) # check results for each round
if modification:
db.load(cn.FB_SEMINARS, s.name, s.to_dict()) # load to database if changes
management_log.info("Changes loaded to database!")
management_log.info(f"permaloop updated in {int((time.time()-checkpoint)*1000)}ms")
# users update
management_log.info("Creating and uploading {0} users".format(len(globals.server.members)-len(globals.users)))
for mem in globals.server.members:
hp.create_user(str(mem.id))
for userid in globals.users.keys():
db.load(cn.FB_USERS, str(userid), vars(globals.users[userid]))
await asyncio.sleep(cn.MINIMAL_CHECKING_DELAY)
event_log.error("Exited infinite loop")
globals.bot.run(os.getenv('TOKEN'))