def clear(): global usrs, gone log.I('CLEAR people tables') usrs = {} gone = {} rewrite() log.I('CLEAR done')
async def get_bans(): log.I('- get bans') global bans, bans_id try: bans = await C.client.get_bans(C.vtm_server) bans_id = set(ban.id for ban in bans) log.I('+ get bans done') except Exception as e: bans_id = set() log.jW("- can't get bans")
def on_final_exit(): log.I('\n', 'People online data:') log.p('\n'.join(people.print_online_people())) ram.t_finish = other.get_now() ram.t_work = (ram.t_finish - ram.t_start) if C.was_Ready: save() log.I('Finally exit at ', ram.t_finish.strftime('[%D %T]'), ', working for ', other.delta2s(ram.t_work)) log.p('====== ' * 10)
def prepare_const(): log.I('- prepare_const') st = os__environ.get('Server_Test') C.is_test = bool(st) and st not in ('0', 'False', 'false', '') C.DISCORD_TOKEN = os__environ.get('DISCORD_TOKEN') if not C.DISCORD_TOKEN: log.E('Config var DISCORD_TOKEN is not defined.') ev.force_exit() if C.is_test: log.jI('Bot work with Test Server') else: C.ignore_channels.update(C.test_channels) tst_server_key = 'TEST_SERVER_ID' tst_channel_key = 'TEST_CHANNEL_ID' vtm_server_key = 'VTM_SERVER_ID' vtm_channel_key = 'WELCOME_CHANNEL_ID' C.VTM_SERVER_ID = os__environ.get(vtm_server_key) C.TST_SERVER_ID = os__environ.get(tst_server_key) if not C.VTM_SERVER_ID: log.E('Config var VTM_SERVER_ID is not defined.') ev.force_exit() if not C.TST_SERVER_ID: log.E('Config var TST_SERVER_ID is not defined.') ev.force_exit() C.WELCOME_CHANNEL_ID = os__environ.get(vtm_channel_key) C.TEST_CHANNEL_ID = os__environ.get(tst_channel_key) if not C.WELCOME_CHANNEL_ID: log.E('Config var WELCOME_CHANNEL_ID is not defined.') ev.force_exit() if not C.TEST_CHANNEL_ID: log.E('Config var TEST_CHANNEL_ID is not defined.') ev.force_exit() C.DATABASE_URL = os__environ.get('DATABASE_URL') if not C.DATABASE_URL: log.E('Config var DATABASE_URL is not defined.') ev.force_exit() C.DROPBOX_ID = os__environ.get('DROPBOX_ID') ram.t_start = other.get_now() log.I('+ prepare_const done')
async def on_voice_state_update_u(before, after): """ :type before: C.types.Member :type after: C.types.Member """ global voice_alert_msg v_old = before.voice_channel v_new = after.voice_channel if v_new: on_user_life_signs(after.id) if v_old == v_new: return if v_old and v_new: log.I('<voice> {0} reconnects from #{1} to #{2}.'.format( after, v_old, v_new)) elif v_old: log.I('<voice> {0} disconnects from #{1}.'.format(after, v_old)) elif v_new: log.I('<voice> {0} connects to #{1}.'.format(after, v_new)) note = com.voice_note(after) if note: log.D('<voice> Note event') await other.type2sent(after, note) await _del_voice_alert(after.id) user = None ch = None if (after.id in C.voice_alert and v_new) or (after.top_role > after.server.me.top_role): user = after ch = v_new # type: C.Types.Channel elif v_old and v_old.voice_members and v_old.voice_members[ 0].id in C.voice_alert: user = v_old.voice_members[0] await _del_voice_alert(user.id) ch = v_old # type: C.Types.Channel if user and ch and len(ch.voice_members) == 1 and not other.s_in_s( ('radio', 'радио'), ch.name.lower()): log.D('<voice> Event to @here') every_prm = ch.overwrites_for(ch.server.default_role) if every_prm.connect or (every_prm.connect is None and ch.server.default_role.permissions.connect): va_ids = voice_alert_ids.setdefault(user.id, []) va_msg = voice_alert_msg.setdefault(user.id, []) va_ids.append( com.write_msg(C.main_ch, text=com.voice_event(user, ch), save_obj=va_msg))
async def _load_messages(): # Load messages in the cache log.I('Start load messages from channels') load_channels = other.get_channels((C.channels['sabbat-charsheets'], ), True) for ch in load_channels: #type: C.Types.Channel if not ch: continue log.I('- load messages from {0.name}'.format(ch)) async for message in C.client.logs_from( ch, limit=10000): #type: C.Types.Message C.client.messages.append(message) log.I('End load messages from channels')
def main_loop(): for sig_name in ('SIGINT', 'SIGTERM'): C.loop.add_signal_handler(getattr(signal, sig_name), functools__partial(ev.on_exit, sig_name)) try: log.I("Start ClientRun.") C.client.run(C.DISCORD_TOKEN) except Exception as e: other.pr_error(e, 'ClientRun', 'Unexpected error') else: log.I("ClientRun is completed without errors.") finally: ev.on_final_exit()
async def on_voice_state_update_o(server, before, after): v_old = before.voice_channel v_new = after.voice_channel if v_old == v_new: return if v_old and v_new: log.I('<voice> {0} reconnects from #{1} to #{2}.'.format( after, v_old, v_new)) elif v_old: log.I('<voice> {0} disconnects from #{1}.'.format(after, v_old)) elif v_new: log.I('<voice> {0} connects to #{1}.'.format(after, v_new))
def prepare_const2(): log.I('- prepare_const2') ev.upd_server() C.vtm_news_ch = other.get_channel(C.channels['vtm_news']) C.vtm_avs_ch = other.get_channel(C.channels['vtm_avs']) C.other_news_ch = other.get_channel(C.channels['other_news']) C.vtm_links_ch = other.get_channel(C.channels['vtm_links']) C.other_links_ch = other.get_channel(C.channels['other_links']) if not (C.vtm_news_ch and C.other_news_ch and C.vtm_links_ch and C.other_links_ch): log.W("Can't find some of helps channels!.") log.I('+ prepare_const2 done')
async def silence_on(name, t=1.0, force=False): """ :param name: string :param t: float :param force: bool :rtype: C.Types.Member """ s = C.prm_server user = other.find_member(s, name) if not user: return None if user.top_role >= s.me.top_role and not force: return 'top_role' if other.has_roles(user, C.roles['protege']): return 'protege' t = max(t, 0.02) if user.id in ram.silence_users: check = ram.silence_users[user.id]['check'] else: check = await turn_silence(user, turn=True, force=force) ram.silence_users[user.id] = {'time': other.get_sec_total() + t * 3600 - 1, 'check': tuple(check)} if not C.is_test: add_roles = [other.find(s.roles, id=C.roles['Silence'])] other.add_roles(user, add_roles, 'silence_on') log.I('Silence on for ', user, ' at ', other.t2s(), ' on ', t, 'h.') return user
def _check_once_in_day(): if not C.is_test: check_t = 7776000 # 90 * 24 * 3600 for uid, usr in people.usrs.items(): t_off = usr.offtime() if t_off > check_t: m = other.find_member(C.vtm_server, uid) if not other.has_roles( m, C.roles_not_for_mortals) and not m.bot and len( m.roles) > 1: other.add_roles(m, C.roles['Mortal'], '_check_once_in_day', by_id=True, server_roles=C.vtm_server.roles) log.I("{} go to Mortal (food)!".format(m)) else: log.I("It's test mode, pass _check_once_in_day")
async def time_sync(): # scan chat and get users time of last_message from history log.I('+ Time_sync start') t = {} mems = [mem.id for mem in C.vtm_server.members] for ch in C.vtm_server.channels: if str(ch.type) == 'text': t[ch.position] = ch channels = [t[k] for k in sorted(t)] log.D('- {0} channels prepare to scan:'.format(len(channels))) for i, ch in enumerate(channels): pr = ch.permissions_for(ch.server.me) if pr.read_message_history: log.D('+ {0}) {1} - check'.format(i + 1, ch.name)) mems_i = set(mems) count = 0 messes = [] async for mess in C.client.logs_from(ch, limit=1000000): messes.append(mess) for mess in messes: aid = mess.author.id if aid in mems_i: ts = other.get_sec_total(mess.timestamp) if ts > usrs[aid].last_m: usrs[aid].last_m = ts usrs[aid].status = 'upd' mems_i.remove(aid) if len(mems_i) < 1: break count += 1 if count % 10000 == 0: log.D('- - <time_sync> check messages: ', count, ', mems_i: ', len(mems_i)) log.D('+ {0}) {1} - done'.format(i + 1, ch.name)) else: log.D('-- {0}) {1} - not permissions for reading'.format( i + 1, ch.name)) log.I('+ Time_sync end') log.jD('Test results:') for mem in C.vtm_server.members: log.jD('{0} \t-\t {1}'.format(mem, other.sec2str(offtime(mem.id))))
async def sync(): # scan chat and get users array from messages in history log.I('+ Sync Start') count = 0 # print('[{0}] TEST'.format(other.t2s())) for mem in C.client.get_all_members(): distribute(mem) async for message in C.client.logs_from(C.main_ch, limit=1000000): distribute(message.author, other.get_sec_total(message.timestamp)) count += 1 if count % 10000 == 0: log.D('<sync> Check message: ', count) # for i in message.raw_mentions: # distribute(await C.client.get_user_info(i), message.timestamp) for usr in bans: distribute(usr) log.D('<sync> MESS COUNT = {0}'.format(str(count))) rewrite() log.I('+ Sync End')
async def tree_test(): import re embr_txt = ( '<@(?P<sir>\d+)> получает право на становление, и <@(?P<child>\d+)> теперь познает всю боль нежизни.', '<@(?P<sir>\d+)> дарует становление, но было ли получено разрешение, и что теперь ждёт новоявленое дитя <@(' '?P<child>\d+)>?', '<@(?P<child>\d+)> теперь находится под защитой клана и теперь за ним присмотрит его сир, <@(?P<sir>\d+)>', 'Не может быть, <@(?P<sir>\d+)> дарует становление неонату <@(?P<child>\d+)> - но является ли это наградой - ' 'или наказанием?', 'Витэ капнуло тут раз - <@(?P<sir>\d+)> теперь... сир у нас. Что ты об этом думаешь, <@(?P<child>\d+)>?', ) r_txt = [] for txt in embr_txt: r_txt.append(re.compile(txt)) ch = other.get_channel('flood') log.D('- <read> for {0}({1}) start'.format(ch, ch.id)) messages = [] count = 0 async for message in C.client.logs_from( ch, limit=1000000000): #type: C.Types.Message if message.author.id == C.users['bot'] and len(message.mentions) == 2: messages.append(message) count += 1 if count % 100 == 0: log.D('- - <read> save messages: ', count) log.D('- <read> end save with {0} messages'.format(count)) messages.reverse() log.D('- <read> start format messages') tree = {} for msg in messages: for r in r_txt: m = r.match(msg.content) if m: d = m.groupdict() tree[d['child']] = d['sir'] log.D(msg.clean_content) log.I(tree) log.I(len(tree))
def load_mem(): log.I('+ load data from memory') not_load = {'t_start', 't_finish', 't_work'} module = sys.modules[ram.__name__] module_attrs = dir(module) variables = set(key for key in module_attrs if key[0] != '_' and not callable(getattr(module, key)) and not hasattr(getattr(module, key), '__dict__')) conn = None try: conn = psycopg2.connect(C.DATABASE_URL, sslmode='require') cur = conn.cursor(cursor_factory=psycopg2.extras.DictCursor) cur.execute("SELECT * FROM memory") rows = cur.fetchall() for row in rows: #print("%s %s %s" % (row["id"], row["var"], row["val"])) if row['var'] in variables and not row['var'] in not_load: try: if row['val'] == 'set()': v = set() else: v = ast__literal_eval(row['val']) except Exception as e: other.pr_error(e, 'load_mem') log.jW("ast.literal_eval can't eval [%s] = '%s'" % (row['var'], row['val'])) else: setattr(module, row['var'], v) except psycopg2.DatabaseError as e: log.E('<ev.load_mem> DatabaseError %s' % e) sys.exit(1) else: log.I('+ memory loaded successfully') finally: if conn: conn.close()
async def on_ready(): ram.debug = C.is_test await other.busy() log.I( f'Logged in as {C.client.user} (id: {C.client.user.id}, Test: {C.is_test})' ) prepare_const2() emj.prepare() await ev.load() ram.debug = ram.debug or C.is_test if not discord__opus.is_loaded(): lb = find_library("opus") log.jD('opus lib: ', lb) # i can't find it on heroku if lb: discord__opus.load_opus(lb) else: log.jI('opus lib not load!') ev.start_timers() log.I('Beckett ready for work now, after starting at ', ram.t_start.strftime('[%d/%m/%y %T]')) log.p('======= ' * 10) await test_fun() # for debugging an testing C.Ready = True await other.test_status(ram.game)
def timer_midnight_update(now=None): if not now: now = other.get_now() log.I('[=== New day! ===]') try: _check_day_ev(now, on_midnight=True) except Exception as e: other.pr_error(e, '_check_day_ev', 'Unexpected error') try: _check_once_in_day() except Exception as e: other.pr_error(e, '_check_once_in_day', 'Unexpected error')
async def delete_reaction(message): """ :type message: C.Types.Message """ # delete reaction typing = data_typings.setdefault(message.channel.id,{}).get(message.id, '') resp = data_msgs.get(message.channel.id, {}).get(message.id, {}) type_ = resp['type'] if resp else '' # if resp and type_ and (not type_.startswith('cmd_') or type_ == 'cmd_help'): if resp and type_ and not type_.startswith('cmd_'): log.I(f'<delete_reaction> [{type_}]') com.rem_from_queue(message.channel.id, typing) for mess in resp['ans']: await com.delete_msg(mess, 'delete_reaction') data_msgs[message.channel.id].pop(message.id)
async def delete_msg(message, reason='-'): """ :param C.Types.Message message: :param reason: string """ log.I( f"Bot will try delete message in #{message.channel.name}. Reason: {reason}." ) try: await C.client.delete_message(message) except C.Exceptions.Forbidden: log.jW("Bot haven't permissions to delete messages here.") except C.Exceptions.NotFound: log.jW("Can't find the message to delete.") except Exception as e: pr_error(e, 'delete_msg', 'Unexpected error')
async def on_member_join_u(member): uid = member.id if uid in ram.silence_users: t = ram.silence_users[uid]['time'] - other.get_sec_total() if t > 0: log.I(member, ' come, but Silence is on.') await manager.silence_on(uid, t / 3600) timer_quarter_h() if people.Usr.check_new(member): not_embrace.add(uid) log.pr_news('{0} ({0.mention}) comeback!'.format(member)) await C.client.send_message( C.main_ch, com.comeback_msg(uid, people.time_out(uid), people.clan(uid))) else: log.pr_news('{0} ({0.mention}) new!'.format(member))
def load_texts_used(): log.D('+ load data_used from DB') conn = None com.d2u.data_used = [] try: conn = psycopg2.connect(C.DATABASE_URL, sslmode='require') cur = conn.cursor(cursor_factory=psycopg2.extras.DictCursor) cur.execute("SELECT * FROM data_used ORDER BY id") rows = cur.fetchall() for row in rows: com.d2u.data_used.append(row['value']) except psycopg2.DatabaseError as e: log.E('<ev.load_texts_used> DatabaseError %s' % e) else: log.I('+ data_used loaded successfully') finally: if conn: conn.close()
def _send_reaction(msg:Msg, m_type, text, edit=False): if not m_type or not text: return log.I(('<reaction.edit>' if edit else '<reaction>') + f'[{m_type}]') if text == 'no-response' or m_type == 'no-response': return if (m_type not in ('rand_tableflip', 'unflip', 'shrug') and ':' not in text and msg.roles.intersection((C.roles['Nosferatu'], C.roles['Malkavian'])) and other.rand() < 0.1): if C.roles['Malkavian'] in msg.roles: text = com.text2malk(text, 1) elif C.roles['Nosferatu'] in msg.roles: text = com.text2leet(text, 0.25) save_obj = _data_msgs_add(msg, m_type) if text != 'no-response' and m_type != 'no-response': _data_tp_add(msg, com.write_msg(msg.channel, text=text, save_obj=save_obj, fun=data_tp_del(msg.channel.id, msg.message.id)))
async def silence_off(name): """ :param name: string :rtype: C.Types.Member """ s = C.prm_server user = other.find_member(s, name) if not user: ram.silence_users.pop(name, 0) return None s_user = ram.silence_users.pop(user.id, False) if s_user: await turn_silence(user, turn=False, check=s_user['check']) if not C.is_test: rem_roles = [other.find(s.roles, id=C.roles['Silence'])] other.rem_roles(user, rem_roles, 'silence_off') log.I('Silence off for ', user, ' at ', other.t2s()) return user else: return False
def phrase_gt(gt=None, uid=None, add_id=None): if not gt: return False uid = uid or 'here' phr = other.choice(d2u.good_time[gt['g_key']][gt['g_type']]['response']) str_weather = '' if gt['g_key'] == 'g_morn': # and uid in emj.morn_add: smile_ids = other.it2list(uid) + other.it2list(add_id) smiles = [] for sm_id in smile_ids: smiles += emj.morn_add.get(sm_id, []) if smiles: phr += ' ' + other.choice(smiles) if uid == C.users['Natali'] and gt['g_key'] in ('g_morn', 'g_day'): try: log.I('try get_weather for Natali') str_weather = '\n:newspaper: ' + get_weather() except Exception as e: other.pr_error(e, 'get_weather') return other.name_phr(uid, phr) + str_weather
def upd_server(): log.I('Update Servers data') C.vtm_server = C.client.get_server(C.VTM_SERVER_ID) # type: C.Types.Server C.tst_server = C.client.get_server(C.TST_SERVER_ID) # type: C.Types.Server if not C.vtm_server: log.E("Can't find server.") force_exit() if not C.tst_server: log.E("Can't find test server.") force_exit() if C.is_test: C.prm_server = C.tst_server C.main_ch = C.client.get_channel(C.TEST_CHANNEL_ID) else: C.prm_server = C.vtm_server C.main_ch = C.client.get_channel(C.WELCOME_CHANNEL_ID) if not C.main_ch: log.E("Can't find welcome_channel.") force_exit()
def rewrite(): usr_rows = [] gn_rows = [] for uid, usr in usrs.items(): usr_rows.append(usr.row_add()) for uid, gn in gone.items(): gn_rows.append(gn.row_add()) conn = None try: ch_usrs_par = ', '.join(Usr.upd_props) ch_usrs_par_s = ', '.join(('%s', ) * len(Usr.upd_props)) ch_gone_par = ', '.join(Gn.upd_props) ch_gone_par_s = ', '.join(('%s', ) * len(Gn.upd_props)) conn = psycopg2.connect(C.DATABASE_URL, sslmode='require') cur = conn.cursor(cursor_factory=psycopg2.extras.DictCursor) cur.execute("TRUNCATE TABLE members RESTART IDENTITY") if usr_rows: query = f'''INSERT INTO members (id, {ch_usrs_par}) VALUES (%s, {ch_usrs_par_s})''' cur.executemany(query, usr_rows) conn.commit() cur.execute("TRUNCATE TABLE users_gone RESTART IDENTITY") if gn_rows: query = f'''INSERT INTO users_gone (id, {ch_gone_par}) VALUES (%s, {ch_gone_par_s})''' cur.executemany(query, gn_rows) conn.commit() except psycopg2.DatabaseError as e: log.E('{{rewrite}} DatabaseError %s' % e) else: log.I('Members rewrite successfully') finally: if conn: conn.close()
async def voting(channel, text='', timeout=60, votes=None, count=3): votes = votes or set() text = text + '\n*(для согласия введите за/y/yes/ok//д/да/+/1/:ok_hand:/:thumbsup:)*' yes = {'за', '1', 'y', 'yes', 'д', 'да', 'ок', 'у', '+', 't_d_', 'ok_hand', 'ok', 'thumbsup', '+1', 'thumbup'} await C.client.send_message(channel, text) time_end = other.get_sec_total() + timeout def check(msg): return (msg.author.id not in votes.union({C.users['bot']}) and yes.intersection(emj.em2text(msg.content).lower().replace('.', '').replace(':', ' ').split())) while len(votes) < count: time_left = time_end - other.get_sec_total() log.I('<voting> We have {0}/{1} votes, wait more for {2} sec.'.format(len(votes), count, time_left)) ans = await C.client.wait_for_message(timeout=time_left, channel=channel, check=check) if ans: votes.add(ans.author.id) other.later_coro(0, C.client.add_reaction(ans, emj.e('ok_hand'))) else: break else: return votes return False
def on_exit(signum): log.I(f'Call on_exit by signal {signum}') C.loop.create_task(C.client.logout()) stop_timers() C.was_Ready = C.Ready C.Ready = False
def _timer_check_silence_in_chat(): if ram.last_vtm_msg and ( (other.get_sec_total() - ram.last_vtm_msg) > 25200): # 7 hours log.I('<timer_quarter_h> silence event!') phr = com.get_t('silence', ) com.write_msg(C.main_ch, phr)
def start_quarter_h_timer(): global timer_quarter_h_handle t = 900 stop_quarter_h_timer() timer_quarter_h_handle = other.later(t, timer_quarter_h) log.I('* Start new timer in {0} seconds.'.format(t))