async def c_replay(self, msg: twitchirc.ChannelMessage): try: args = arg_parser.parse_args( main.delete_spammer_chrs(msg.text), { 'time': datetime.timedelta, 'channel': str, arg_parser.POSITIONAL: str # ignore }) except arg_parser.ParserError as e: return f'@{msg.user}, Error: {e}' if args['time'] is ...: args['time'] = datetime.timedelta(seconds=30) if args['channel'] is ...: args['channel'] = msg.channel if args['channel'] != msg.channel: j_data = await self.get_user(args['channel']) if 'data' not in j_data: return f'@{msg.user}, API error (in get-users).' if len(j_data['data']) == 0: return f'@{msg.user}, failed to find user.' user = j_data['data'][0]['id'] else: user = msg.flags['room-id'] async with aiohttp.request( 'get', 'https://api.twitch.tv/helix/videos', params={ 'user_id': user, 'sort': 'time', 'first': 1 }, headers={ 'Authorization': f'Bearer {main.twitch_auth.json_data["access_token"]}', 'Client-ID': main.twitch_auth.json_data["client_id"] }) as request: j_data = await request.json() print(j_data) if 'data' not in j_data: return f'@{msg.user}, API error (in get-videos).' if len(j_data['data']) == 0: return f'@{msg.user}, failed to find stream.' length = arg_parser.handle_typed_argument( j_data['data'][0]['duration'], datetime.timedelta) if args['time'] > length: return f'@{msg.user}, FeelsWeirdMan It is not possible to create a replay of before the stream started.' t: datetime.timedelta = length - args['time'] return j_data['data'][0]['url'] + f'?t={math.floor(t.seconds / 3600):0>.0f}h' \ f'{math.floor((t.seconds % 3600) / 60):0>.0f}m' \ f'{math.floor((t.seconds % 3600) % 60):0>.0f}s'
async def command_debug(msg: twitchirc.ChannelMessage): if isinstance(msg, twitchirc.ChannelMessage): return f'@{msg.user}, This command is only available in whispers :)' argv = arg_parser.parse_args( main.delete_spammer_chrs(msg.text).rstrip(' '), { 0: str, 1: str }) if argv[0] is ...: return f'debug what?' debugee_type = argv[0] print(repr(debugee_type), debugee_type == 'command', debugee_type == 'user', debugee_type == 'me', debugee_type in ('user', 'me')) if debugee_type == 'command': if argv[1] is ...: return f'debug which command?' cmd_name = argv[1] matches = list( filter(lambda c: c.chat_command == cmd_name, main.bot.commands)) if not matches: fake_msg = twitchirc.ChannelMessage(cmd_name, msg.user, msg.channel, parent=main.bot) matches = list( filter( lambda c: c.matcher_function and c.matcher_function( fake_msg, c), main.bot.commands)) if not matches: return f'Unknown command {cmd_name}' if len(matches) == 1: return _debug_command(matches[0]) else: return f'@{msg.user}, {len(matches)} found.' elif debugee_type in ('user', 'me'): if argv[1] is ... and debugee_type != 'me': return f'@{msg.user}, how do I debug?' if debugee_type == 'me': argv[1] = msg.user users = main.User.get_by_name(argv[1]) if users: return _debug_user(users[0]) else: return f'@{msg.user}, couldn\'t find the target user.' else: return f'@{msg.user}, how to debug {debugee_type!r}?'
async def c_nuke_url(self, msg: twitchirc.ChannelMessage): try: args = arg_parser.parse_args(main.delete_spammer_chrs(msg.text), { 'dry-run': bool, 'url': str, 'perma': bool, 'force': bool, 'timeout': datetime.timedelta, 'hastebin': bool }, defaults={ 'dry-run': False, 'perma': False, 'hastebin': False, 'timeout':..., 'force': False }) except arg_parser.ParserError as e: return f'@{msg.user}, error: {e.message}' if args['url'] is ... or (args['timeout'] is ... and not args['perma']): print(args) return f'@{msg.user}, url, timeout (or perma) are required parameters' url = args['url'] if url.startswith(plugin_hastebin.hastebin_addr): url = url.replace(plugin_hastebin.hastebin_addr, plugin_hastebin.hastebin_addr + 'raw/') async with aiohttp.request('get', url) as req: if req.status != 200: return f'@{msg.user}, failed to download user list :(' if req.content_type == 'text/plain': users, reasons = self.parse_user_list( (await req.text('utf-8')).replace('\r\n', '\n').replace( '$(newline)', '\n')) while '' in users: users.remove('') return await self.nuke( args, msg, users, force_nuke=args['force'], disable_hastebinning=not args['hastebin'], reasons=reasons) else: return f'Refusing to use data, bad content type: {req.content_type}, expected text/plain'
async def c_unnuke(self, msg: twitchirc.ChannelMessage): try: args = arg_parser.parse_args(main.delete_spammer_chrs(msg.text), { 'dry-run': bool, 'url': str, 'perma': bool, 'force': bool, 'hastebin': bool }, defaults={ 'dry-run': False, 'perma': False, 'force': False, 'hastebin': False }) except arg_parser.ParserError as e: return f'@{msg.user}, error: {e.message}' if args['url'] is ...: return f'@{msg.user}, url is a required parameter' if args['perma'] is ...: args['perma'] = False url = args['url'] if url.startswith(plugin_hastebin.hastebin_addr): url = url.replace(plugin_hastebin.hastebin_addr, plugin_hastebin.hastebin_addr + 'raw/') async with aiohttp.request('get', url) as req: if req.status != 200: return f'@{msg.user}, failed to download user list :(' if req.content_type == 'text/plain': users, _ = self.parse_user_list( (await req.text('utf-8')).replace('\r\n', '\n').replace( '$(newline)', '\n')) while '' in users: users.remove('') return await self.unnuke( args, msg, users, disable_hastebinning=(not args['hastebin']))
async def c_nuke_url(self, msg: twitchirc.ChannelMessage): try: args = arg_parser.parse_args( main.delete_spammer_chrs(msg.text), { 'dry-run': bool, 'url': str, 'perma': bool, 'force': bool, 'timeout': datetime.timedelta }) except arg_parser.ParserError as e: return f'@{msg.user}, error: {e.message}' arg_parser.check_required_keys( args, ('url', 'timeout', 'search', 'perma', 'dry-run')) if args['url'] is ... or (args['timeout'] is ... and not args['perma']) or args['search'] is ...: return f'@{msg.user}, url, timeout and search are required parameters' if args['perma'] is ...: args['perma'] = False url = args['url'] if url.startswith(plugin_hastebin.hastebin_addr): url = url.replace(plugin_hastebin.hastebin_addr, plugin_hastebin.hastebin_addr + 'raw/') async with aiohttp.request('get', url) as req: if req.status != 200: return f'@{msg.user}, failed to download user list :(' if req.content_type == 'text/plain': users = (await req.text('utf-8')).replace('\r\n', '\n').replace( '$(newline)', '\n').split('\n') while '' in users: users.remove('') return await self.nuke(args, msg, users, force_nuke=args['force'])
async def c_nuke(self, msg: twitchirc.ChannelMessage): try: args = arg_parser.parse_args( main.delete_spammer_chrs(msg.text), { 'regex': str, 'perma': bool, 'timeout': datetime.timedelta, 'search': datetime.timedelta, 'dry-run': bool, 'force': bool, 'hastebin': bool }) except arg_parser.ParserError as e: return f'@{msg.user}, error: {e.message}' arg_parser.check_required_keys( args, ('regex', 'timeout', 'search', 'perma', 'dry-run', 'force')) if args['regex'] is ... or args['timeout'] is ... or args[ 'search'] is ...: return f'@{msg.user}, regex, timeout and search are required parameters' if args['perma'] is ...: args['perma'] = False try: r = regex.compile(args['regex']) except Exception as e: return f'@{msg.user}, error while compiling regex: {e}' results = plugin_chat_cache.find_messages( msg.channel, expr=r, min_timestamp=time.time() - args['search'].total_seconds()) if not results: return f'@{msg.user}, found no messages matching the regex.' else: return await self.nuke_from_messages( args, msg, results, disable_hastebinning=not args['hastebin'])
async def command_whois(msg: util_bot.StandardizedMessage): if not bots or bots_invalidates <= time.time(): await _load_bots() try: args = arg_parser.parse_args(util_bot.delete_spammer_chrs(msg.text), { 'id': int, 'name': str, 'channels': str, 'verbose': bool, 1: str }, defaults={ 'id': None, 'name': None, 'channels': '', 'verbose': False, 1: None }) except arg_parser.ParserError as err: return f'@{msg.user}, {err.message}' print(args) if args['name'] and args[1]: return f'@{msg.user}, Name argument provided twice.' if args[1]: args['name'] = args[1] if args['id'] is not None: name = args['id'] id_ = True elif args['name'] is not None: name = args['name'] id_ = False if name.startswith('#'): id_ = True name = name.lstrip('#') else: return (f'@{msg.user} {msg.text.split(" ")[0]} (name:TWITCH_USERNAME|id:TWITCH_ID) [+verbose] OR ' f'{msg.text.split(" ")[0]} TWITCH_USERNAME [+verbose]') params = {} if id_: params['id'] = name.lower() else: params['login'] = name.lower() async with aiohttp.request('get', f'https://api.ivr.fi/v2/twitch/user', params=params, headers={ 'User-Agent': util_bot.constants.USER_AGENT }) as request: print(request) if request.status == 400: return f'@{msg.user} Bad username.' if request.status == 404: return f'@{msg.user} No such user found.' users: typing.List[IVRUser] = await request.json( loads=lambda *largs, **kwargs: json.loads(*largs, **kwargs, object_hook=_object_hook) ) if len(users) == 0: return f'@{msg.user} No such user found.' data: IVRUser = users[0] roles = '' if data.roles.affiliate: roles += 'affiliate, ' if data.roles.partner: roles += 'partner, ' # rip site admin flag if any(i.set_id == 'admin' for i in data.badges): roles += 'site admin (badge), ' # if data.roles.get('isSiteAdmin', False): # roles += 'site admin, ' if data.roles.staff: roles += 'staff, ' if roles == '': roles = 'none' roles = (roles[::-1].replace(', '[::-1], '', 1))[::-1] # replace last ", " with "". if data.display_name.casefold() != data.login.casefold(): login = f'({data.login})' else: login = '' print(data) created_at_str = '' for i in data.created_at: if i == '.': break created_at_str += i created_at_str = created_at_str.rstrip('Z') created_on = datetime.datetime.strptime(created_at_str, '%Y-%m-%dT%H:%M:%S') logo_warning = '' if data.logo.startswith('https://static-cdn.jtvnw.net/user-default-pictures'): logo_warning = 'avatar: DEFAULT, ' bot_notes = '' bot = _find_bot(data.login) if bot is not None: if args['verbose']: last_active = f', Last active: {bot["lastSeen"][:-4]}' if bot['lastSeen'] is not None else '' else: last_active = '' bot_notes = (f'Prefix: {bot["prefix"] if bot["prefix"] is not None else "<blank>"}, ' f'Description: {bot["description"] if bot["description"] is not None else "<blank>"}, ' f'Language: {bot["language"] if bot["language"] is not None else "<unknown>"}' f'{last_active}') user = f'user {data.display_name}{login}' if ' ' in data.display_name: user = f'user {data.display_name!r}{login}' info = [ user, logo_warning, f'chat color: {data.chat_color if data.chat_color else "never set"}', f'account created at {created_on}', f'roles: {roles}', f'id: {data.id}', f'bio: {data.bio}' if data.bio is not None else 'empty bio', bot_notes, ] special_warnings = getattr(data, 'special_warning', '') if special_warnings: info.append(special_warnings) if args['verbose']: info.append(f'badges: {", ".join(b.set_id + "/" + b.version for b in data.badges)}') info_text = '' long_text = '' for elem in info: info_text += f'{elem.strip(",. ")}, ' if elem else '' long_text += f' - {elem.strip(",. ")}\n' if elem else '' ret_val = (f'@{msg.user}, {"BANNED " if data.banned else ""}{"bot " if data.verified_bot else ""}' + info_text.rstrip('., ')) if len(ret_val) > 500: url = plugin_hastebin.hastebin_addr + await plugin_hastebin.upload( long_text ) return f'@{msg.user}, Command output was too long, here\'s a hastebin: {url}' else: return ret_val
def command_manage_blacklists(self, msg: twitchirc.ChannelMessage): argv = main.delete_spammer_chrs(msg.text).rstrip(' ').split(' ', 1) if len(argv) == 1: return f'@{msg.user}, {plugin_help.all_help[plugin_help.SECTION_COMMANDS]["plonk"]}' text = argv[1] try: kw = arg_parser.parse_args(text, { 'scope': str, 'user': str, 'command': str, 'expires': datetime.timedelta }, defaults={ 'scope': None, 'user': None, 'command': None, 'expires': None }) except arg_parser.ParserError as e: return e.message if kw['command'] is None: return f'@{msg.user}, No `command:...` provided.' if kw['user'] is None: return f'@{msg.user}, No `user:...` provided.' kw['user'] = kw['user'].lower() if kw['user'] == 'everyone': kw['user'] = True kw['scope'] = kw['scope'].lower().strip('#') if kw['scope'] != 'global': sc = [] for ch in kw['scope'].split(','): ch = ch.lstrip('#').lower() if ch not in main.bot.channels_connected: return f'@{msg.user}, Invalid `scope`: {kw["scope"]!r}, no such channel.' sc.append(ch) kw['scope'] = sc if kw['command'].lower() == 'all': kw['command'] = True else: cmd = None for i in main.bot.commands: i: main.Command if i.chat_command.lower( ) == kw['command'] or kw['command'] in i.aliases: cmd = i.chat_command if cmd is None: return f'@{msg.user}, Invalid `command`: {kw["command"]!r}. No such command exists.' else: kw['command'] = cmd del cmd if kw['expires']: kw['expires'] = datetime.datetime.now() + kw['expires'] with main.session_scope() as session: if kw['scope'] == 'global': targets = main.User.get_by_name( kw['user'], session) if kw['user'] is not True else None if targets is None or len(targets) == 1: obj = BlacklistEntry( target=targets[0] if targets is not None else targets, command=kw['command'] if kw['command'] is not True else None, channel=None, expires_on=kw['expires'], is_active=True) blacklists.append(obj) session.add(obj) elif len(targets) == 0: return f'@{msg.user} Failed to find user: {kw["user"]}' else: return f'@{msg.user} Found multiple users possible with name {kw["user"]}' else: for ch in kw['scope']: targets = main.User.get_by_name( kw['user'], session) if kw['user'] is not True else None channels = main.User.get_by_name(ch, session) if len(channels) == 1: if targets is None or len(targets) == 1: obj = BlacklistEntry( target=targets[0] if targets is not None else targets, command=kw['command'] if kw['command'] is not True else None, channel=channels[0], expires_on=kw['expires'], is_active=True) blacklists.append(obj) session.add(obj) elif len(targets) == 0: return f'@{msg.user} Failed to find user: {kw["user"]}' else: return f'@{msg.user} Found multiple users possible with name {kw["user"]}' elif len(channels) == 0: return f'@{msg.user} Failed to find channel: {ch}' elif len(channels) > 1: return f'@{msg.user} Found multiple channels possible with name {ch}' return f'@{msg.user}, Added blacklist for command {kw["command"]} with scope {kw["scope"]} for {kw["user"]}'