async def join_channel(self, c: Channel) -> bool: """Attempt to add `self` to `c`.""" if self in c: # user already in the channel. if glob.config.debug: log(f'{self} was double-added to {c}.') return False if not self.priv & c.read: log(f'{self} tried to join {c} but lacks privs.') return False # lobby can only be interacted with while in mp lobby. if c._name == '#lobby' and not self.in_lobby: return False c.append(self) # Add to channels self.channels.append(c) # Add to player self.enqueue(packets.channelJoin(c.name)) # update channel usercounts for all clients that can see. # for instanced channels, enqueue update to only players # in the instance; for normal channels, enqueue to all. targets = c.players if c.instance else glob.players for p in targets: p.enqueue(packets.channelInfo(*c.basic_info)) if glob.config.debug: log(f'{self} joined {c}.') return True
async def join_channel(self, c: Channel) -> bool: if self in c: # User already in the channel. await plog(f'{self} tried to double join {c}.') return False if not self.priv & c.read: await plog(f'{self} tried to join {c} but lacks privs.') return False # Lobby can only be interacted with while in mp lobby. if c._name == '#lobby' and not self.in_lobby: return False c.append(self) # Add to channels self.channels.append(c) # Add to player self.enqueue(await packets.channelJoin(c.name)) # Update channel usercounts for all clients that can see. # For instanced channels, enqueue update to only players # in the instance; for normal channels, enqueue to all. targets = c.players if c.instance else glob.players for p in targets: p.enqueue(await packets.channelInfo(*c.basic_info)) await plog(f'{self} joined {c}.') return True
def leave_channel(self, c: Channel) -> None: if self not in c: printlog(f'{self} tried to leave {c} but is not in it.') return c.remove(self) # Remove from channels self.channels.remove(c) # Remove from player self.enqueue(packets.channelKick(c.name)) printlog(f'{self} left {c}.')
async def revokechannel(self, ctx, channel: discord.Channel = None): if channel is None: channel = ctx.message.channel _channel = Channel(channel.id) if _channel.is_Registered: _channel.remove_channel() await self.bot.say(self.msg.CHANNEL_REVOKED.format(channel.name)) else: await self.bot.say(self.errors.CHANNEL_NOT_IN_DB)
async def registerchannel(self, ctx, channel: discord.Channel = None): if channel is None: channel = ctx.message.channel _channel = Channel(channel.id) if _channel.is_Registered: await self.bot.say(self.errors.CHANNEL_ALREADY_IN_DB) return else: _channel.append_channel() await self.bot.say(self.msg.CHANNEL_REGISTERED.format(channel.name))
async def run_server(addr: cmyui.Address) -> None: glob.version = cmyui.Version(2, 7, 0) glob.http = aiohttp.ClientSession(json_serialize=orjson.dumps) loop = asyncio.get_event_loop() try: loop.add_signal_handler(signal.SIGINT, lambda: loop.stop()) loop.add_signal_handler(signal.SIGTERM, lambda: loop.stop()) except NotImplementedError: pass glob.db = cmyui.AsyncSQLPoolWrapper() await glob.db.connect(**glob.config.mysql) # Aika glob.bot = Player(id = 1, name = 'Aika', priv = Privileges.Normal) glob.bot.ping_time = 0x7fffffff await glob.bot.stats_from_sql_full() # no need to get friends await glob.players.add(glob.bot) # Add all channels from db. async for chan in glob.db.iterall('SELECT * FROM channels'): await glob.channels.add(Channel(**chan)) async with cmyui.AsyncTCPServer(addr) as glob.serv: plog(f'Gulag v{glob.version} online!', Ansi.LGREEN) async for conn in glob.serv.listen(glob.config.max_conns): asyncio.create_task(handle_conn(conn))
async def on_start() -> None: glob.http = aiohttp.ClientSession(json_serialize=orjson.dumps) # connect to mysql glob.db = cmyui.AsyncSQLPool() await glob.db.connect(glob.config.mysql) # run the sql updater updater = Updater(glob.version) await updater.run() await updater.log_startup() # create our bot & append it to the global player list. glob.bot = Player(id=1, name='Aika', priv=Privileges.Normal) glob.bot.last_recv_time = float(0x7fffffff) glob.players.append(glob.bot) # add all channels from db. async for chan in glob.db.iterall('SELECT * FROM channels'): chan['read_priv'] = Privileges(chan.pop('read_priv', 1)) chan['write_priv'] = Privileges(chan.pop('write_priv', 2)) glob.channels.append(Channel(**chan)) # add all mappools from db. async for pool in glob.db.iterall('SELECT * FROM tourney_pools'): # overwrite basic types with some class types creator = await glob.players.get(id=pool['created_by'], sql=True) pool['created_by'] = creator # replace id with player object pool = MapPool(**pool) await pool.maps_from_sql() glob.pools.append(pool) # add new donation ranks & enqueue tasks to remove current ones. # TODO: this system can get quite a bit better; rather than just # removing, it should rather update with the new perks (potentially # a different tier, enqueued after their current perks). async def rm_donor(userid: int, delay: int): await asyncio.sleep(delay) p = await glob.players.get(id=userid, sql=True) await p.remove_privs(Privileges.Donator) log(f"{p}'s donation perks have expired.", Ansi.MAGENTA) query = ('SELECT id, donor_end FROM users ' 'WHERE donor_end > UNIX_TIMESTAMP()') async for donation in glob.db.iterall(query): # calculate the delta between now & the exp date. delta = donation['donor_end'] - time.time() if delta > (60 * 60 * 24 * 30): # ignore donations expiring in over a months time; # the server should restart relatively often anyways. continue asyncio.create_task(rm_donor(donation['id'], delta))
async def handle(self, p: Player) -> None: # TODO: match validation..? if p.silenced: p.enqueue(packets.matchJoinFail() + packets.notification( 'Multiplayer is not available while silenced.')) return if not glob.matches.append(self.match): # failed to create match (match slots full). p.send('Failed to create match (no slots available).', sender=glob.bot) p.enqueue(packets.matchJoinFail()) return # create the channel and add it # to the global channel list as # an instanced channel. chan = Channel(name=f'#multi_{self.match.id}', topic=f"MID {self.match.id}'s multiplayer channel.", auto_join=False, instance=True) glob.channels.append(chan) self.match.chat = chan await p.update_latest_activity() p.join_match(self.match, self.match.passwd) log(f'{p} created a new multiplayer match.')
async def channelinfo(self, ctx, channel: discord.Channel = None): if channel is None: channel = ctx.message.channel _channel = Channel(channel.id) if _channel.is_Registered is False: await self.bot.say(self.errors.CHANNEL_NOT_IN_DB) return _data = _channel.info target = channel.mention is_nsfw = _data[0][1] if is_nsfw == 0: is_nsfw = "False" else: is_nsfw = "True" persistence = _data[0][2] embed = discord.Embed(title=self.msg.E_CINFO_TITLE, description=self.msg.E_CINFO_DESC, color=self.themes.MAIN_COL) embed.add_field(name=self.msg.E_CINFO_CHAN, value=target, inline=False) embed.add_field(name=self.msg.E_CINFO_ID, value=channel.id, inline=False) embed.add_field(name=self.msg.E_CINFO_NSFW, value=is_nsfw, inline=False) embed.add_field(name=self.msg.E_CINFO_PERSIST, value=persistence, inline=False) await self.bot.say(embed=embed)
async def run_server(addr: Address) -> None: glob.version = Version(2, 8, 5) glob.http = aiohttp.ClientSession(json_serialize=orjson.dumps) loop = asyncio.get_event_loop() try: loop.add_signal_handler(signal.SIGINT, loop.stop) loop.add_signal_handler(signal.SIGTERM, loop.stop) except NotImplementedError: pass glob.db = AsyncSQLPoolWrapper() await glob.db.connect(**glob.config.mysql) # create our bot & append it to the global player list. glob.bot = Player(id=1, name='Aika', priv=Privileges.Normal) glob.bot.last_recv_time = 0x7fffffff glob.players.add(glob.bot) # add all channels from db. async for chan in glob.db.iterall('SELECT * FROM channels'): await glob.channels.add(Channel(**chan)) # run background process to # disconnect inactive clients. loop.create_task(disconnect_inactive()) async with AsyncTCPServer(addr) as glob.serv: log(f'Gulag v{glob.version} online!', AnsiRGB(0x00ff7f)) async for conn in glob.serv.listen(glob.config.max_conns): loop.create_task(handle_conn(conn))
async def on_message_delete(self, message): _channel = Channel(message.channel.id) _server = Server(message.server.id) if _channel.is_log_blocked: return if _server.logging_allowed is False: return if _server.logs_channel is None: return embed = discord.Embed(title="❌ | Message DELETED", color=self.themes.MAIN_COL, timestamp=message.timestamp) embed.set_author(name=message.author, icon_url=message.author.avatar_url) embed.add_field(name="User ID", value=message.author.id, inline=False) embed.add_field(name="Channel", value=message.channel.mention, inline=False) embed.add_field(name="Message", value=f"\u200b{message.content}", inline=False) logs = discord.utils.get(message.server.channels, id=_server.logs_channel) await self.bot.send_message(logs, embed=embed)
async def start(self, message: types.Message): logger.info(message.chat.id) if not message.chat.id in self.channels.keys(): self.channels[message.chat.id] = Channel(message.chat.id) logger.info(self.channels) await self.bot.send_message(message.chat.id, text=settings.start_text, parse_mode='Markdown', reply_markup=start.start())
def join_channel(self, c: Channel) -> bool: if self in c: printlog(f'{self} tried to double join {c}.') return False if not self.priv & c.read: printlog(f'{self} tried to join {c} but lacks privs.') return False # Lobby can only be interacted with while in mp lobby. if c._name == '#lobby' and not self.in_lobby: return False c.append(self) # Add to channels self.channels.append(c) # Add to player self.enqueue(packets.channelJoin(c.name)) printlog(f'{self} joined {c}.') return True
async def prepare(cls) -> None: """Fetch data from sql & return; preparing to run the server.""" log('Fetching channels from sql', Ansi.LCYAN) return cls( Channel(name=row['name'], topic=row['topic'], read_priv=Privileges(row['read_priv']), write_priv=Privileges(row['write_priv']), auto_join=row['auto_join'] == 1) for row in await glob.db.fetchall('SELECT * FROM channels'))
async def prepare(cls, db_cursor: aiomysql.DictCursor) -> 'Channels': """Fetch data from sql & return; preparing to run the server.""" log('Fetching channels from sql.', Ansi.LCYAN) await db_cursor.execute('SELECT * FROM channels') return cls([ Channel(name=row['name'], topic=row['topic'], read_priv=Privileges(row['read_priv']), write_priv=Privileges(row['write_priv']), auto_join=row['auto_join'] == 1) async for row in db_cursor ])
async def setpersistence(self, ctx, value: int, channel: discord.Channel = None): if channel is None: channel = ctx.message.channel _channel = Channel(channel.id) if _channel.is_Registered is False: await self.bot.say(self.errors.CHANNEL_NOT_IN_DB) return rng = random.randint(100, 999) embed = discord.Embed(title=self.msg.E_VERIFY_TITLE, description=self.msg.E_VERIFY_DESC.format(rng), color=self.themes.MAIN_COL) embed.add_field(name=self.msg.E_VERIFY_BEFORE, value=_channel.persistence, inline=False) embed.add_field(name=self.msg.E_VERIFY_PERSIST, value=value, inline=False) embed.add_field(name=self.msg.E_VERIFY_CHANGING, value=channel.name, inline=False) await self.bot.say(embed=embed) msg = await self.bot.wait_for_message(author=ctx.message.author, content=str(rng), timeout=20.00) if msg is None: await self.bot.say("Confirmation timed out, please try again.") return _channel.set_persistence(value) await self.bot.say(self.msg.OPERATION_SUCCESSFUL)
async def prepare(cls, db_cursor: aiomysql.DictCursor) -> "Channels": """Fetch data from sql & return; preparing to run the server.""" log("Fetching channels from sql.", Ansi.LCYAN) await db_cursor.execute("SELECT * FROM channels") return cls([ Channel( name=row["name"], topic=row["topic"], read_priv=Privileges(row["read_priv"]), write_priv=Privileges(row["write_priv"]), auto_join=row["auto_join"] == 1, ) async for row in db_cursor ], )
def add_spectator(self, p) -> None: chan_name = f'#spec_{self.id}' if not (c := glob.channels.get(chan_name)): # Spec channel does not exist, create it and join. glob.channels.add( Channel(name=chan_name, topic=f"{self.name}'s spectator channel.'", read=Privileges.Normal, write=Privileges.Normal, auto_join=False, temp=True)) c = glob.channels.get(chan_name)
async def create_match(user: Player, p: bytes) -> None: match = (reader.handle_packet(p, (('match', osuTypes.match),)))['match'] glob.matches[match.id] = match if not glob.matches.get(match.id): return user.enqueue(writer.matchJoinFail()) mp_chan = Channel(name='#multiplayer', desc=f'Multiplayer channel for match ID {match.id}', auto=False, perm=False) glob.channels[f'#multi_{match.id}'] = mp_chan match.chat = mp_chan user.join_match(match, match.pw) log(f'{user.name} created a new multiplayer lobby.', Ansi.LBLUE)
async def add_spectator(self, p: 'Player') -> None: chan_name = f'#spec_{self.id}' if not (c := glob.channels[chan_name]): # spectator chan doesn't exist, create it. spec_chan = Channel(name=chan_name, topic=f"{self.name}'s spectator channel.'", read=Privileges.Normal, write=Privileges.Normal, auto_join=False, instance=True) await glob.channels.add(spec_chan) c = glob.channels[chan_name]
async def create_match(user: Player, p: bytes) -> None: match = (reader.handle_packet(p, (("match", osuTypes.match),)))["match"] glob.matches[match.id] = match if not glob.matches.get(match.id): return user.enqueue(writer.matchJoinFail()) mp_chan = Channel( name="#multiplayer", desc=f"Multiplayer channel for match ID {match.id}", auto=False, perm=False, ) glob.channels[f"#multi_{match.id}"] = mp_chan match.chat = mp_chan user.join_match(match, match.pw) base_info(f"{user.name} created a new multiplayer lobby.")
async def togglelogblock(self, ctx, channel: discord.Channel = None): if channel is None: channel = ctx.message.channel _channel = Channel(channel.id) if _channel.is_Registered is False: await self.bot.say(self.errors.CHANNEL_NOT_IN_DB) return if _channel.is_log_blocked: _channel.toggle_log_block(False) await self.bot.say(self.msg.LOG_UNBLOCKED.format(channel.name)) else: _channel.toggle_log_block(True) await self.bot.say(self.msg.LOG_BLOCKED.format(channel.name))
async def handle(self, p: Player) -> None: if not glob.matches.append(self.match): # failed to create match (match slots full). await p.send(glob.bot, 'Failed to create match (no slots available).') p.enqueue(packets.matchJoinFail()) return # create the channel and add it # to the global channel list as # an instanced channel. chan = Channel(name=f'#multi_{self.match.id}', topic=f"MID {self.match.id}'s multiplayer channel.", auto_join=False, instance=True) glob.channels.append(chan) self.match.chat = chan await p.update_latest_activity() await p.join_match(self.match, self.match.passwd) log(f'{p} created a new multiplayer match.')
async def run_server(loop: uvloop.Loop, addr: cmyui.Address): glob.version = cmyui.Version(2, 2, 8) glob.http = aiohttp.ClientSession(json_serialize=orjson.dumps) glob.db = cmyui.AsyncSQLPool() await glob.db.connect(loop, **glob.config.mysql) # Aika glob.bot = Player(id=1, name='Aika', priv=Privileges.Normal) glob.bot.ping_time = 0x7fffffff await glob.bot.stats_from_sql_full() # no need to get friends await glob.players.add(glob.bot) # Add all channels from db. async for chan in glob.db.iterall('SELECT * FROM channels'): await glob.channels.add(Channel(**chan)) async with cmyui.AsyncTCPServer(addr) as serv: await plog(f'Gulag v{glob.version} online!', Ansi.LIGHT_GREEN) async for conn in serv.listen(loop, glob.config.max_conns): asyncio.create_task(handle_conn(conn))
async def togglensfw(self, ctx, channel: discord.Channel = None): if channel is None: channel = ctx.message.channel _channel = Channel(channel.id) if _channel.is_Registered is False: await self.bot.say(self.errors.CHANNEL_NOT_IN_DB) return if _channel.is_NSFW: _channel.toggle_nsfw(False) await self.bot.say( self.msg.CHANNEL_REVOKED_NSFW.format(channel.name)) else: _channel.toggle_nsfw(True) await self.bot.say(self.msg.CHANNEL_IS_NSFW.format(channel.name))
async def on_message_edit(self, before, after): try: _channel = Channel(before.channel.id) _server = Server(before.server.id) if _channel.is_log_blocked: return if _server.logging_allowed is False: return if _server.logs_channel is None: return embed = discord.Embed(title="〽 | Message EDITED", color=self.themes.MAIN_COL, timestamp=before.timestamp) embed.set_author(name=before.author, icon_url=before.author.avatar_url) embed.add_field(name="User ID", value=before.author.id, inline=False) embed.add_field(name="Channel", value=before.channel.mention, inline=False) embed.add_field(name="Message Before", value=before.content, inline=False) embed.add_field(name="Message After", value=after.content, inline=False) logs = discord.utils.get(before.server.channels, id=_server.logs_channel) await self.bot.send_message(logs, embed=embed) except: pass
async def on_message(self, message): try: channel = message.channel _channel = Channel(channel.id) _server = Server(message.server.id) if _channel.is_log_blocked: return if _server.logging_allowed is False: return if _server.logs_channel is None: return attach = message.attachments[0]['url'] embed = discord.Embed(title="📷 | Image Sent", color=self.themes.MAIN_COL, timestamp=message.timestamp) embed.set_author(name=message.author, icon_url=message.author.avatar_url) embed.add_field(name="User ID", value=message.author.id, inline=False) embed.add_field(name="Channel", value=message.channel.mention, inline=False) embed.add_field(name="Image URL", value=attach, inline=False) embed.set_image(url=attach) logs = discord.utils.get(message.server.channels, id=_server.logs_channel) await self.bot.send_message(logs, embed=embed) except: pass
async def registerallchannels(self, ctx, persistence: int): await self.bot.say(self.msg.REG_WAIT) count = 0 for channel in ctx.message.server.channels: _channel = Channel(channel.id) if _channel.is_Registered: continue _channel.append_channel() _channel.set_persistence(persistence) count += 1 if count == 0: await self.bot.say(self.msg.NO_CHANNEL_REG) return await self.bot.say(self.msg.REG_CHAN_SUCC.format(count))
async def on_message(self, message): _user = User(message.author.id) _channel = Channel(message.channel.id) if message.author.bot is True: return if _user.is_Registered is False: return if _user.exp_toggled is False: return if _channel.is_Registered is False: return _user.add_experience(_channel.persistence) level = _user.level next_lvl_exp = 25 * level * level - 25 * level if _user.experience > next_lvl_exp: _user.level_up if level == 1: return else: self.helpers.level_up_img(message.author.avatar_url, _user.level) msg = await self.bot.send_file( message.channel, 'externals/img/temp/leveltemp.png', content=self.msg.LEVEL_UP.format(message.author.mention, level + 1)) await asyncio.sleep(20) await self.bot.delete_message(msg)
return False else: # match is being created slotID = 0 # add to our global match list; # this will generate a match id. await glob.matches.add(m) # create the channel and add it # to the global channel list as # an instanced channel. match_chan = Channel(name=f'#multi_{m.id}', topic=f"MID {m.id}'s multiplayer channel.", read=Privileges.Normal, write=Privileges.Normal, auto_join=False, instance=True) await glob.channels.add(match_chan) m.chat = glob.channels[f'#multi_{m.id}'] if not await self.join_channel(m.chat): log(f'{self} failed to join {m.chat}.') return False if (lobby := glob.channels['#lobby']) in self.channels: await self.leave_channel(lobby) slot = m.slots[0 if slotID == -1 else slotID]