async def manageAutoJoin(args: ManageBotArgs) -> bool: db: DatabaseMain if len(args.message) < 3: return False if len(args.message) < 4: return False bannedWithReason: Optional[str] async with DatabaseMain.acquire() as db: bannedWithReason = await db.isChannelBannedReason( args.message.lower[3]) if bannedWithReason is not None: channel: str = args.message.lower[3] args.send(f'Chat {channel} is banned from joining') return True if args.message.lower[2] in ['add', 'insert', 'join']: async with DatabaseMain.acquire() as db: return await autojoin.auto_join_add( db, args.message.lower[3], args.send) if args.message.lower[2] in ['del', 'delete', 'rem', 'remove', 'part']: async with DatabaseMain.acquire() as db: return await autojoin.auto_join_delete( db, args.message.lower[3], args.send) if args.message.lower[2] in ['pri', 'priority']: priority: int = 0 with suppress(ValueError, IndexError): priority = int(args.message[4]) return await auto_join_priority( args.message.lower[3], priority, args.send) return False
async def updateQuote(channel: str, nick: str, quoteId: int, quote: str) -> bool: db: DatabaseMain cursor: aioodbc.cursor.Cursor async with DatabaseMain.acquire() as db, await db.cursor() as cursor: query: str if db.isPostgres: query = ''' UPDATE quotes SET quote=?, document=to_tsvector(?) WHERE quoteId=? AND broadcaster=? ''' await cursor.execute(query, (quote, quote, quoteId, channel)) else: query = ''' UPDATE quotes SET quote=? WHERE quoteId=? AND broadcaster=? ''' await cursor.execute(query, (quote, quoteId, channel)) if cursor.rowcount == 0: return False query = ''' INSERT INTO quotes_history (quoteId, createdTime, broadcaster, quote, editor) VALUES (?, CURRENT_TIMESTAMP, ?, ?, ?) ''' await cursor.execute(query, (quoteId, channel, quote, nick)) await db.commit() return True
async def commandSetAutoPurge(args: ChatCommandArgs) -> bool: twitchUser: str = args.message.lower[1] nick: str = args.message[1] db: DatabaseMain cursor: aioodbc.cursor.Cursor async with DatabaseMain.acquire() as db, await db.cursor() as cursor: query: str = '''\ SELECT 1 FROM auto_purge WHERE broadcaster=? AND twitchUser=? ''' await cursor.execute(query, (args.chat.channel, twitchUser)) row: Optional[Tuple[int]] = await cursor.fetchone() if row is None: value: bool = False if len(args.message) >= 3: response: parser.Response value = bool( parser.get_response(args.message.lower[2], default=parser.Yes)) query = ''' INSERT INTO auto_purge (broadcaster, twitchUser, stopcommands) VALUES (?, ?, ?) ''' await cursor.execute(query, (args.chat.channel, twitchUser, value)) args.chat.send(f'Enabled Auto-Purge on {nick}') else: query = ''' DELETE FROM auto_purge WHERE broadcaster=? AND twitchUser=? ''' await cursor.execute(query, (args.chat.channel, twitchUser)) args.chat.send(f'Disabled Auto-Purge on {nick}') await db.commit() await library.reset_auto_purges(args.chat.channel, args.data) return True
async def addQuote(channel: str, nick: str, quote: str) -> int: db: DatabaseMain cursor: aioodbc.cursor.Cursor async with DatabaseMain.acquire() as db, await db.cursor() as cursor: query: str quoteId: int if db.isPostgres: query = ''' INSERT INTO quotes (broadcaster, quote, document) VALUES (?, ?, to_tsvector(?)) ''' await cursor.execute(query, (channel, quote, quote)) await cursor.execute('SELECT lastval()') quoteId = int((await cursor.fetchone() or [0])[0]) else: query = ''' INSERT INTO quotes (broadcaster, quote) VALUES (?, ?) ''' await cursor.execute(query, (channel, quote)) await cursor.execute('SELECT last_insert_rowid()') quoteId = int((await cursor.fetchone() or [0])[0]) query = ''' INSERT INTO quotes_history (quoteId, createdTime, broadcaster, quote, editor) VALUES (?, CURRENT_TIMESTAMP, ?, ?, ?) ''' await cursor.execute(query, (quoteId, channel, quote, nick)) await db.commit() return quoteId
async def insert_banned_channel(channel: str, reason: str, nick: str, send: Send) -> bool: if channel == bot.config.botnick: send('Cannot ban the bot itself') return True result: bool db: DatabaseMain async with DatabaseMain.acquire() as db: bannedWithReason: Optional[str] bannedWithReason = await db.isChannelBannedReason(channel) if bannedWithReason is not None: send(f'{channel} is already banned for: {bannedWithReason}') return True result = await db.addBannedChannel(channel, reason, nick) if result: db.discardAutoJoin(channel) utils.partChannel(channel) msg: str if result: msg = f'Chat {channel} is now banned' else: msg = f'Chat {channel} could not be banned. Error has occured.' send(msg) return True
async def commandFfzSlots(args: ChatCommandArgs) -> bool: if 'slotsLock' not in args.chat.sessionData: args.chat.sessionData['slotsLock'] = asyncio.Lock() lock: asyncio.Lock = args.chat.sessionData['slotsLock'] if lock.locked(): utils.whisper(args.nick, f'Channel cooldown (3.0 seconds)') return True db: DatabaseMain with await lock: async with DatabaseMain.acquire() as db: isBot: bool = await library.isSlotBots( db, args.chat.channel, args.nick, args.timestamp - library.unbotCooldown) lastAttempt: datetime = await library.getLastFfzSlotsUser( db, args.chat.channel, args.nick) if await library.in_cooldown(db, args.chat.channel, args.nick, args.timestamp, lastAttempt, isBot): return False lastAttempts: List[datetime] lastAttempts = await library.getLastFfzSlotsAttempts( db, args.chat.channel, args.nick, args.timestamp - library.logBotAttempts) markedBot: Optional[bool] = await library.process_bot( db, args.chat.channel, args.nick, args.timestamp, lastAttempt, isBot, lastAttempts) emotes: Optional[Dict[int, str]] emotes = await library.generate_ffz_pool(args.chat, args.data) length: int = 3 emoteIds: List[int] = list(emotes.keys()) selected: List[int] = [random.choice(emoteIds) for _ in range(length)] numMatching: int = 0 emoteId: int for emoteId in selected: if emoteId == selected[0]: numMatching += 1 allMatching: bool = numMatching == 3 selectedEmotes = ' | '.join(emotes[i] for i in selected) args.chat.send(f'{args.nick} -> {selectedEmotes}') if allMatching: args.chat.send(f'{args.nick} has won !ffzslots') if markedBot is True: args.chat.send(f'''\ {args.nick} is now considered as a bot. His cooldown is increased to 20 \ minutes.''') if markedBot is False: args.chat.send(f'''\ {args.nick} is now considered not as a bot. His \cooldown is back to 2 \ minutes.''') await library.recordFfzSlots(db, args.chat.channel, args.nick, emotes, selected) return True
async def getTagsOfQuote(quoteId: int) -> Set[str]: db: DatabaseMain cursor: aioodbc.cursor.Cursor async with DatabaseMain.acquire() as db, await db.cursor() as cursor: query: str = ''' SELECT tag FROM quotes_tags WHERE quoteId=? ''' return {t async for t, in await cursor.execute(query, (quoteId, ))}
async def getQuoteById(channel: str, id: int) -> Optional[str]: db: DatabaseMain cursor: aioodbc.cursor.Cursor async with DatabaseMain.acquire() as db, await db.cursor() as cursor: query: str = ''' SELECT quote FROM quotes WHERE broadcaster=? AND quoteId=? ''' await cursor.execute(query, (channel, id)) return (await cursor.fetchone() or [None])[0]
async def getRandomQuote(channel: str) -> Optional[str]: db: DatabaseMain cursor: aioodbc.cursor.Cursor async with DatabaseMain.acquire() as db, await db.cursor() as cursor: query: str = ''' SELECT quote FROM quotes WHERE broadcaster=? ORDER BY random() LIMIT 1 ''' await cursor.execute(query, (channel, )) return (await cursor.fetchone() or [None])[0]
async def getAnyQuoteById(id: int) -> Tuple[Optional[str], Optional[str]]: db: DatabaseMain cursor: aioodbc.cursor.Cursor async with DatabaseMain.acquire() as db, await db.cursor() as cursor: query: str = ''' SELECT quote, broadcaster FROM quotes WHERE quoteId=? ''' await cursor.execute(query, (id, )) row: Optional[Tuple[str, str]] = await cursor.fetchone() return (row[0], row[1]) if row else (None, None)
async def getAnyRandomQuote() -> Tuple[Optional[str], Optional[str]]: db: DatabaseMain cursor: aioodbc.cursor.Cursor async with DatabaseMain.acquire() as db, await db.cursor() as cursor: query: str = ''' SELECT quote, broadcaster FROM quotes ORDER BY random() LIMIT 1 ''' await cursor.execute(query) row: Optional[Tuple[str, str]] = await cursor.fetchone() return (row[0], row[1]) if row else (None, None)
async def deleteTagsToQuote(quoteId: int, tags: List[str]) -> bool: db: DatabaseMain cursor: aioodbc.cursor.Cursor async with DatabaseMain.acquire() as db, await db.cursor() as cursor: query: str = ''' DELETE FROM quotes_tags WHERE quoteId=? AND tag=? ''' await cursor.executemany(query, map(lambda t: (quoteId, t), tags)) await db.commit() return bool(tags)
async def deleteQuote(channel: str, quoteId: int) -> bool: db: DatabaseMain cursor: aioodbc.cursor.Cursor async with DatabaseMain.acquire() as db, await db.cursor() as cursor: query: str = ''' DELETE FROM quotes WHERE quoteId=? AND broadcaster=? ''' await cursor.execute(query, (quoteId, channel)) await db.commit() return cursor.rowcount != 0
async def addTagsToQuote(quoteId: int, tags: List[str]) -> bool: db: DatabaseMain cursor: aioodbc.cursor.Cursor async with DatabaseMain.acquire() as db, await db.cursor() as cursor: query: str = ''' INSERT INTO quotes_tags (quoteId, tag) VALUES (?, ?) ''' await cursor.executemany(query, map(lambda t: (quoteId, t), tags)) await db.commit() return bool(tags)
async def list_banned_channels(send: Send) -> bool: bannedChannels: Iterable[str] db: DatabaseMain async with DatabaseMain.acquire() as db: bannedChannels = [channel async for channel in db.listBannedChannels()] if bannedChannels: send(message.messagesFromItems(bannedChannels, 'Banned Channels: ')) else: send('There are no banned channels') return True
async def auto_join_priority(channel: str, priority: int, send: Send) -> bool: result: bool db: DatabaseMain async with DatabaseMain.acquire() as db: result = await db.setAutoJoinPriority(channel, priority) if result: send(f'Auto join for {channel} is set to priority {priority}') else: send(f'Auto join for {channel} was never enabled') return True
async def get_auto_purges_db(broadcaster: str) -> Dict[str, bool]: db: DatabaseMain cursor: aioodbc.cursor.Cursor purges: Dict[str, bool] = {} async with DatabaseMain.acquire() as db, await db.cursor() as cursor: query: str = ''' SELECT twitchUser, stopcommands FROM auto_purge WHERE broadcaster=? ''' user: str stop: bool async for user, stop in await cursor.execute(query, (broadcaster,)): purges[user] = stop return purges
async def copyQuote(from_channel: str, to_channel: str, nick: str, quoteId: int) -> Optional[int]: db: DatabaseMain cursor: aioodbc.cursor.Cursor async with DatabaseMain.acquire() as db, await db.cursor() as cursor: query: str = ''' SELECT quote FROM quotes WHERE quoteId=? AND broadcaster=? ''' await cursor.execute(query, (quoteId, from_channel)) quote: Optional[str] = ((await cursor.fetchone()) or [None])[0] if quote is None: return None newQuoteId: int if db.isPostgres: query = ''' INSERT INTO quotes (broadcaster, quote, document) VALUES (?, ?, to_tsvector(?)) ''' await cursor.execute(query, (to_channel, quote, quote)) await cursor.execute('SELECT lastval()') newQuoteId = int((await cursor.fetchone() or [0])[0]) else: query = ''' INSERT INTO quotes (broadcaster, quote) VALUES (?, ?) ''' await cursor.execute(query, (to_channel, quote)) await cursor.execute('SELECT last_insert_rowid()') newQuoteId = int((await cursor.fetchone() or [0])[0]) query = ''' SELECT tag FROM quotes_tags WHERE quoteId=? ''' quoteTagsParams: List[Tuple[int, str]] quoteTagsParams = [ (newQuoteId, t) async for t, in await cursor.execute(query, (quoteId, )) ] if quoteTagsParams: query = ''' INSERT INTO quotes_tags (quoteId, tag) VALUES (?, ?) ''' await cursor.executemany(query, quoteTagsParams) query = ''' INSERT INTO quotes_history (quoteId, createdTime, broadcaster, quote, editor) VALUES (?, CURRENT_TIMESTAMP, ?, ?, ?) ''' await cursor.execute(query, (newQuoteId, to_channel, quote, nick)) await db.commit() return newQuoteId
async def come(channel: str, send: Send) -> bool: bannedWithReason: Optional[str] priority: Union[float, int] db: DatabaseMain async with DatabaseMain.acquire() as db: bannedWithReason = await db.isChannelBannedReason(channel) if bannedWithReason is not None: send(f'Chat {channel} is banned from joining') return True priority = await db.getAutoJoinsPriority(channel) joinResult: bool = utils.joinChannel(channel, priority) if joinResult: send(f'Joining {channel}') else: send(f'I am already in {channel}') return True
async def auto_join(channel: str, send: Send, message: Message) -> bool: db: DatabaseMain async with DatabaseMain.acquire() as db: bannedWithReason: Optional[str] bannedWithReason = await db.isChannelBannedReason(channel) if bannedWithReason is not None: send(f'Chat {channel} is banned from joining') return True if len(message) >= 2: removeMsgs: List[str] = [ '0', 'false', 'no', 'remove', 'rem', 'delete', 'del', 'leave', 'part' ] if message.lower[1] in removeMsgs: return await auto_join_delete(db, channel, send) return await auto_join_add(db, channel, send)
async def join(channel: str, send: Send) -> bool: priority: Union[int, float] db: DatabaseMain async with DatabaseMain.acquire() as db: bannedWithReason: Optional[str] bannedWithReason = await db.isChannelBannedReason(channel) if bannedWithReason is not None: send(f'Chat {channel} is banned from joining') return True priority = await db.getAutoJoinsPriority(channel) if utils.joinChannel(channel, priority): send(f'Joining {channel}') else: send(f'Already joined {channel}') return True
async def getRandomQuoteBySearch(channel: str, words: Sequence[str]) -> Optional[str]: db: DatabaseMain cursor: aioodbc.cursor.Cursor async with DatabaseMain.acquire() as db, await db.cursor() as cursor: query: str params: Tuple[str, ...] if db.isPostgres: query = ''' SELECT quoteId FROM quotes WHERE broadcaster=? AND document @@ to_tsquery(?) UNION SELECT quoteId FROM quotes q WHERE broadcaster=? AND ''' + ' AND '.join([ '? IN (SELECT LOWER(tag) FROM quotes_tags AS t ' 'WHERE t.quoteId=q.quoteId)' ] * len(words)) query = ''' SELECT quote FROM quotes WHERE quoteId=( SELECT quoteId FROM (%s) AS q ORDER BY RANDOM() LIMIT 1)''' % query params = (( channel, ' | '.join(words), channel, ) + tuple(w.lower() for w in words)) await cursor.execute(query, params) return (await cursor.fetchone() or [None])[0] query = ''' SELECT quoteId FROM quotes WHERE broadcaster=? AND ''' + ' AND '.join(['quote LIKE ?'] * len(words)) + ''' UNION SELECT quoteId FROM quotes q WHERE broadcaster=? AND ''' + ' AND '.join([ '? IN (SELECT LOWER(tag) FROM quotes_tags AS t ' 'WHERE t.quoteId=q.quoteId)' ] * len(words)) query = ''' SELECT quote FROM quotes WHERE quoteId=( SELECT quoteId FROM (%s) AS q ORDER BY RANDOM() LIMIT 1)''' % query params = ((channel, ) + tuple(f'%{w}%' for w in words) + (channel, ) + tuple(w.lower() for w in words)) await cursor.execute(query, params) return (await cursor.fetchone() or [None])[0]
async def getAnyRandomQuoteBySearch( words: Sequence[str]) -> Tuple[Optional[str], Optional[str]]: db: DatabaseMain cursor: aioodbc.cursor.Cursor async with DatabaseMain.acquire() as db, await db.cursor() as cursor: query: str params: Tuple[str, ...] row: Optional[Tuple[str, str]] if db.isPostgres: query = ''' SELECT quoteId FROM quotes WHERE document @@ to_tsquery(?) UNION SELECT quoteId FROM quotes q WHERE ''' + ' AND '.join([ '? IN (SELECT LOWER(tag) FROM quotes_tags AS t ' 'WHERE t.quoteId=q.quoteId)' ] * len(words)) query = ''' SELECT quote, broadcaster FROM quotes WHERE quoteId=( SELECT quoteId FROM (%s) AS q ORDER BY RANDOM() LIMIT 1)''' % query params = ((' | '.join(words), ) + tuple(w.lower() for w in words)) await cursor.execute(query, params) row = await cursor.fetchone() return (row[0], row[1]) if row else (None, None) query = ''' SELECT quoteId FROM quotes WHERE 1=1 AND ''' + ' AND '.join(['quote LIKE ?'] * len(words)) + ''' UNION SELECT quoteId FROM quotes q WHERE 1=1 AND ''' + ' AND '.join([ '? IN (SELECT LOWER(tag) FROM quotes_tags AS t ' 'WHERE t.quoteId=q.quoteId)' ] * len(words)) query = f''' SELECT quote, broadcaster FROM quotes WHERE quoteId=( SELECT quoteId FROM ({query}) AS q ORDER BY RANDOM() LIMIT 1) ''' params = (tuple(f'%{w}%' for w in words) + tuple(w.lower() for w in words)) await cursor.execute(query, params) row = await cursor.fetchone() return (row[0], row[1]) if row else (None, None)
async def delete_banned_channel(channel: str, reason: str, nick: str, send: Send) -> bool: result: bool db: DatabaseMain async with DatabaseMain.acquire() as db: bannedWithReason: Optional[str] bannedWithReason = await db.isChannelBannedReason(channel) if bannedWithReason is None: send(f'{channel} is not banned') return True result = await db.removeBannedChannel(channel, reason, nick) msg: str if result: msg = f'Chat {channel} is now unbanned' else: msg = f'Chat {channel} could not be unbanned. Error has occured.' send(msg) return True
async def getQuoteIdsByWords(channel: str, words: Sequence[str]) -> List[int]: db: DatabaseMain cursor: aioodbc.cursor.Cursor async with DatabaseMain.acquire() as db, await db.cursor() as cursor: query: str params: Tuple[str, ...] if db.isPostgres: query = ''' SELECT quoteId FROM quotes WHERE broadcaster=? AND document @@ to_tsquery(?) UNION SELECT quoteId FROM quotes q WHERE broadcaster=? AND ''' + ' AND '.join([ '? IN (SELECT LOWER(tag) FROM quotes_tags AS t ' 'WHERE t.quoteId=q.quoteId)' ] * len(words)) query = ''' SELECT quoteId FROM (%s) AS q ORDER BY quoteId ASC ''' % query params = (( channel, ' | '.join(words), channel, ) + tuple(w.lower() for w in words)) await cursor.execute(query, params) return [i async for i, in await cursor.execute(query, params)] query = ''' SELECT quoteId FROM quotes WHERE broadcaster=? AND ''' + ' AND '.join(['quote LIKE ?'] * len(words)) + ''' UNION SELECT quoteId FROM quotes q WHERE broadcaster=? AND ''' + ' AND '.join([ '? IN (SELECT LOWER(tag) FROM quotes_tags AS t ' 'WHERE t.quoteId=q.quoteId)' ] * len(words)) query = 'SELECT quoteId FROM (' + query + ') AS q ORDER BY quoteId ASC' params = ((channel, ) + tuple(f'%{w}%' for w in words) + (channel, ) + tuple(w.lower() for w in words)) return [i async for i, in await cursor.execute(query, params)]
async def commandSlots(args: ChatCommandArgs) -> bool: if 'slotsLock' not in args.chat.sessionData: args.chat.sessionData['slotsLock'] = asyncio.Lock() lock: asyncio.Lock = args.chat.sessionData['slotsLock'] if lock.locked(): utils.whisper(args.nick, f'Channel cooldown (3.0 seconds)') return True db: DatabaseMain with await lock: async with DatabaseMain.acquire() as db: isBot: bool = await library.isSlotBots( db, args.chat.channel, args.nick, args.timestamp - library.unbotCooldown) lastAttempt: datetime = await library.getLastTwitchSlotsUser( db, args.chat.channel, args.nick) if await library.in_cooldown(db, args.chat.channel, args.nick, args.timestamp, lastAttempt, isBot): return False lastAttempts: List[datetime] lastAttempts = await library.getLastTwitchSlotsAttempts( db, args.chat.channel, args.nick, args.timestamp - library.logBotAttempts) markedBot: Optional[bool] = await library.process_bot( db, args.chat.channel, args.nick, args.timestamp, lastAttempt, isBot, lastAttempts) emotes: Optional[Dict[int, str]] emotes = await library.generate_twitch_pool(args.data) length: int = 3 emoteIds: List[int] = list(emotes.keys()) selected: List[int] = [random.choice(emoteIds) for _ in range(length)] matchEmoteId: int = selected[0] numMatching: int = 0 emoteId: int for emoteId in selected: if emoteId == matchEmoteId: numMatching += 1 allMatching: bool = numMatching == 3 selectedEmotes: str = ' | '.join(emotes[i] for i in selected) msg: str = f'{args.nick} -> {selectedEmotes}' args.chat.send(msg) if allMatching: args.chat.send(f'{args.nick} has won !slots') if matchEmoteId == 25 and args.permissions.chatModerator: args.chat.send(f'.timeout {args.nick} 1') args.chat.send('Thanks for winning the Kappa!') dbTimeout: DatabaseTimeout async with DatabaseTimeout.acquire() as dbTimeout: await dbTimeout.recordTimeout( args.chat.channel, args.nick, None, 'slots', None, 1, str(args.message), msg) if markedBot is True: args.chat.send(f'''\ {args.nick} is now considered as a bot. His cooldown is increased to 20 \ minutes.''') if markedBot is False: args.chat.send(f'''\ {args.nick} is now considered not as a bot. His cooldown is back to 2 \ minutes.''') await library.recordTwitchSlots(db, args.data, args.chat.channel, args.nick, emotes, selected) return True
async def commandMultiTwitch(args: ChatCommandArgs) -> bool: ''' Example Commands: !multitwitch << gives a link of linked multitwitch, available to everyone !multitwitch kadgar << available to everyone !multitwitch preference kadgar << available to everyone !multitwitch add !multitwitch add kappa !multitwitch drop !multitwitch reset !multitwitch remove kappa !multitwitch remove !multitwitch event kappa << owner command, does not perform auto removal The command should automatically remove inactive streams of more than 5 minutes or 15 minutes after the initial add if stream hasnt started ''' # TODO: mypy fix after https://github.com/python/mypy/issues/1855 currentTime: datetime = utils.now() db: DatabaseMain cursor: aioodbc.cursor query: str params: Tuple[Any, ...] paramsM: List[Tuple[Any, ...]] row: Tuple[Any, ...] group: str groupO: Optional[str] groups: List[Tuple[Any, ...]] event: Optional[bool] async with DatabaseMain.acquire() as db, await db.cursor() as cursor: if (len(args.message) < 2 or not args.permissions.moderator or args.message.lower[1] in library.multiUrls): cooldown: timedelta = timedelta(seconds=30) if args.permissions.moderator: cooldown = timedelta(seconds=10) if (not args.permissions.broadcaster and 'multitwitch' in args.chat.sessionData): since: timedelta since = currentTime - args.chat.sessionData['multitwitch'] if since < cooldown: return False query = 'SELECT twitchgroup FROM multitwitch WHERE broadcaster=?' await cursor.execute(query, (args.chat.channel, )) groupO = (await cursor.fetchone() or [None])[0] twitches: List[str] = [] if groupO: query = ''' SELECT broadcaster, addedTime, lastLive FROM multitwitch WHERE twitchgroup=? ORDER BY isEvent DESC, addedTime ASC ''' async for row in await cursor.execute(query, (groupO, )): broadcaster: str added: datetime live: datetime broadcaster, added, live = row if live is None: if currentTime - added > library.addedCooldown: continue else: if currentTime - live > library.liveCooldown: continue twitches.append(broadcaster) if not twitches: args.chat.send(f'https://www.twitch.tv/{args.chat.channel}') if args.permissions.moderator: args.chat.send('''\ Just do !multitwitch add <twitch user> to create/start a multitwitch link''') args.chat.sessionData['multitwitch'] = currentTime return True default: str = await args.data.getChatProperty( args.chat.channel, 'multitwitch', library.default, str) preference: str = await args.data.getChatProperty( args.nick, 'multitwitch', default, str) if (len(args.message) >= 2 and args.message.lower[1] in library.multiUrls): preference = args.message.lower[1] if len(twitches) == 1: args.chat.send('https://www.twitch.tv/' + twitches[0]) elif preference in library.multiUrls: args.chat.send(library.multiUrls[preference](twitches)) else: args.chat.send(library.multiUrls[library.default](twitches)) args.chat.sessionData['multitwitch'] = currentTime return True if args.message.lower[1] == 'preference': if len(args.message) < 2: await args.data.setChatProperty(args.nick, 'multitwitch', None) elif args.message.lower[2] in library.multiUrls: await args.data.setChatProperty(args.nick, 'multitwitch', args.message.lower[2]) else: args.chat.send('Unrecognized multitwitch site') if not args.permissions.moderator: return False if args.message.lower[1] == 'add': other: str if len(args.message) < 3: other = args.nick else: other = args.message.lower[2] if not await args.data.twitch_is_valid_user(other): args.chat.send(f'{other} is not a valid Twitch user') return True if other == args.chat.channel: args.chat.send('You cannot add yourself for multitwitch link') return True query = ''' SELECT broadcaster, twitchgroup, isEvent FROM multitwitch WHERE broadcaster IN (?, ?)''' params = args.chat.channel, other groups = [row async for row in await cursor.execute(query, params)] if len(groups) == 0: alphabet = ('0123456789' 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' 'abcdefghijklmnopqrstuvwxyz') group = ''.join(random.choice(alphabet) for i in range(7)) query = ''' INSERT INTO multitwitch (broadcaster, twitchgroup, addedTime) VALUES (?, ?, ?)''' paramsM = [(args.chat.channel, group, currentTime), (other, group, currentTime)] await cursor.executemany(query, paramsM) args.chat.send(f'''\ Created a multitwitch for {args.chat.channel} and {other}''') elif len(groups) == 1: group = groups[0][1] toAdd: str done: str toAdd = args.chat.channel if groups[0][0] == other else other done = args.chat.channel if groups[0][0] != other else other query = ''' INSERT INTO multitwitch (broadcaster, twitchgroup, addedTime) VALUES (?, ?, ?)''' await cursor.execute(query, (toAdd, group, currentTime)) args.chat.send(f'''\ Added {toAdd} to the multitwitch of {done} and others''') else: group = groups[0][1] g: Tuple[str, str, bool] for g in cast(List[Tuple[str, str, bool]], groups): if g[2]: group = g[1] break query = ''' UPDATE multitwitch SET twitchgroup=? WHERE twitchgroup=?''' paramsM = [(g[1], group) for g in groups if g[1] != group] if not paramsM: args.chat.send(f'''\ {args.chat.channel} and {other} are already in the same multitwitch''') return True await cursor.executemany(query, paramsM) args.chat.send(f'''\ Merged the multitwitches of {args.chat.channel} and {other}''') await db.commit() if args.message.lower[1] in ['drop', 'delete', 'del' 'remove', 'rem']: who: str if len(args.message) < 3: who = args.chat.channel else: who = args.message.lower[2] query = ''' SELECT twitchgroup, isEvent FROM multitwitch WHERE twitchgroup=(SELECT twitchgroup FROM multitwitch WHERE broadcaster=?) AND broadcaster=?''' await cursor.execute(query, (args.chat.channel, who)) groupO, event = await cursor.fetchone() or (None, None) if groupO is None: args.chat.send(f'''\ Multitwitch of {who} does not exist or is not part of the same multitwitch of \ {args.chat.channel}''') return True query = 'SELECT COUNT(*) FROM multitwitch WHERE twitchgroup=?' await cursor.execute(query, (groupO, )) if (await cursor.fetchone())[0] <= 2: query = 'DELETE FROM multitwitch WHERE twitchgroup=?' await cursor.execute(query, (groupO, )) if args.chat.channel == who: args.chat.send(f'''\ Reset the multitwitch of {args.chat.channel} and others''') else: args.chat.send(f'''\ Reset the multitwitch of {args.chat.channel} and {who}''') await db.commit() return True if not event: query = 'DELETE FROM multitwitch WHERE broadcaster=?' await cursor.execute(query, (who, )) if who == args.chat.channel: args.chat.send(f'''\ Removed {args.chat.channel} from a multitwitch''') else: args.chat.send(f'''\ Removed {who} from a multitwitch with {args.chat.channel}''') await db.commit() return True query = ''' SELECT COUNT(*) FROM multitwitch WHERE twitchgroup=? AND isEvent=FALSE UNION ALL SELECT COUNT(*) FROM multitwitch WHERE twitchgroup=? AND isEvent=TRUE''' await cursor.execute(query, (groupO, ) * 2) notEvent, = await cursor.fetchone() inEvent, = await cursor.fetchone() if notEvent > 0: args.chat.send(f'''\ Cannot remove {who} until all non-event users are removed''') return True query = 'DELETE FROM multitwitch WHERE broadcaster=?' await cursor.execute(query, (who, )) if who == args.chat.channel: args.chat.send(f'''\ Removed {args.chat.channel} from a multitwitch''') else: args.chat.send(f'''\ Removed {who} from a multitwitch with {args.chat.channel}''') await db.commit() if args.message.lower[1] == 'reset': query = 'SELECT twitchgroup FROM multitwitch WHERE broadcaster=?' await cursor.execute(query, (args.chat.channel, )) groupO, = await cursor.fetchone() or (None, ) if groupO is None: args.chat.send(f'''\ Multitwitch of {args.chat.channel} does not exist''') return True query = ''' SELECT COUNT(*) FROM multitwitch WHERE twitchgroup=? AND isEvent=FALSE UNION ALL SELECT COUNT(*) FROM multitwitch WHERE twitchgroup=? AND isEvent=TRUE''' await cursor.execute(query, (groupO, ) * 2) notEvent, = await cursor.fetchone() inEvent, = await cursor.fetchone() if notEvent > 0 and inEvent > 1: query = ''' DELETE FROM multitwitch WHERE twitchgroup=? AND isEvent=FALSE''' await cursor.execute(query, (groupO, )) args.chat.send('Reset the multitwitch of non-event users') await db.commit() return True query = 'DELETE FROM multitwitch WHERE twitchgroup=?' await cursor.execute(query, (groupO, )) args.chat.send(f'''\ Reset the multitwitch of {args.chat.channel} and others''') await db.commit() if args.message.lower[1] == 'event' and args.permissions.owner: if len(args.message) < 3: who = args.chat.channel else: who = args.message.lower[2] query = ''' SELECT twitchgroup, isEvent FROM multitwitch WHERE twitchgroup=(SELECT twitchgroup FROM multitwitch WHERE broadcaster=?) AND broadcaster=?''' await cursor.execute(query, (args.chat.channel, who)) groupO, event = await cursor.fetchone() or (None, None) if groupO is not None: query = 'UPDATE multitwitch SET isEvent=? WHERE broadcaster=?' await cursor.execute(query, ( not event, who, )) if not event: args.chat.send(f'{who} is marked as an event multitwitch') else: args.chat.send( f'{who} is unmarked from an event multitwitch') await db.commit() else: args.chat.send(f'''\ Multitwitch of {who} does not exist or is not part of the same multitwitch of \ {args.chat.channel}''') return True