def c_ping_output(self, msg: twitchirc.ChannelMessage): cd_state = main.do_cooldown('ping_optout', msg, global_cooldown=0, local_cooldown=30) if cd_state: return if msg.user in self.ping_optouts: del self.ping_optouts[msg.user] return f'@{msg.user}, you can be pinged by the bot now.' args = main.delete_spammer_chrs(msg.text).rstrip(' ').split(' ', 1) if len(args) == 1: main.bot.send( msg.reply( f'@{msg.user}, please select a mode. Available modes: ' f'replacement (will replace ping with [PING]), ' f'invisibles (will add invisible characters to pings), ' f'no@ (will remove the @)')) return args = args[1] if self._in(['replace', 'replacement', 'repl', 'r'], args.lower()): mode = OPTOUT_MODE_REPLACEMENT elif self._in( ['invis', 'invisibles', 'invisible', 'characters', 'i', 'chars'], args.lower()): mode = OPTOUT_MODE_INVISIBLE_CHARACTERS elif self._in(['at', 'noat', '@', 'no@', 'n@', 'no_at'], args.lower()): mode = OPTOUT_MODE_NO_AT # noinspection PyUnboundLocalVariable self.ping_optouts[msg.user] = mode return f'@{msg.user}, i will no longer ping you :)'
def command_check_suggestion(msg: twitchirc.ChannelMessage): cd_state = main.do_cooldown('check_suggestion', msg, global_cooldown=0, local_cooldown=30) if cd_state: return t = main.delete_spammer_chrs(msg.text).split(' ') if len(t) == 1: return f'@{msg.user}, Usage: check_suggestion <ID> or check_suggestion.' target = t[1] if target.isnumeric(): target = int(target) with main.session_scope() as session: suggestion = session.query(Suggestion).filter(Suggestion.id == target).first() if suggestion is None: return f'@{msg.user} Suggestion id {target!r} not found.' else: return (f'@{msg.user} ' f'{suggestion.humanize(suggestion.author.last_known_username == msg.user)}') else: with main.session_scope() as session: user = main.User.get_by_message(msg, no_create=True) if user is None: return f'@{msg.user}, You are a new user, you don\'t have any suggestions.' suggestions = (session.query(Suggestion) .filter(Suggestion.author == user) .filter(Suggestion.state.notin_([Suggestion.SuggestionState.done, Suggestion.SuggestionState.rejected, Suggestion.SuggestionState.not_a_suggestion]))) return (f'@{msg.user} Your suggestions: ' f'{", ".join([f"{s.id} ({s.nice_state()})" for s in suggestions])}')
def c_pyramid(self, msg: twitchirc.ChannelMessage): if not self._get_pyramid_enabled(msg.channel): return f'@{msg.user}, This command is disabled here.' cd_state = main.do_cooldown('pyramid', msg, global_cooldown=60, local_cooldown=60) if cd_state: return t = main.delete_spammer_chrs(msg.text).split(' ', 1) if len(t) == 1: return f'@{msg.user}, Usage: pyramid <size> <text...>' args = t[1].rstrip() size = '' for arg in args.split(' '): arg: str if arg.isnumeric(): size = arg break # prefer first number! if size != '': args: str = args.replace(size, '', 1).rstrip() + ' ' size = int(size) if not args.strip(' '): return f'@{msg.user}, Nothing to send. NaM' for i in range(1, size): main.bot.send(msg.reply(args * i)) for i in range(size, 0, -1): main.bot.send(msg.reply(args * i))
def command_ping_simple(msg: twitchirc.ChannelMessage): cd_state = main.do_cooldown('ping', msg, global_cooldown=int(1.5 * 60), local_cooldown=2 * 60) if cd_state: return return (f'@{msg.user} PONG! Bot has been running for ' f'{datetime.timedelta(seconds=round(main.uptime().total_seconds()))}. ' f'{len(main.bot.commands)} ' f'commands registered. {_blacklist_info(msg.channel)}{_channel_info()}')
def command_vote(msg: twitchirc.ChannelMessage): if msg.user in ['__vote_info', '__vote_results']: return f'@{get_user_name(msg)} No crashing the bot for you ;)' return cd_state = main.do_cooldown('vote', msg, global_cooldown=0, local_cooldown=1 * 60) if cd_state: return if msg.channel not in votes: return f"@{get_user_name(msg)} There's no on-going vote in this channel." else: vote_obj: Vote = votes[msg.channel]['__vote_info'] if vote_obj.closed: return f"@{get_user_name(msg)} There's no on-going vote in this channel." argv = msg.text.split(' ') if len(argv) < 2: return f'@{get_user_name(msg)} Usage: !vote <option(s)>' already_voted = msg.user in votes[msg.channel] if votes[msg.channel]['__vote_info'].allow_multiple: user_votes = argv[1].split(',') if ',' in argv[1] else [argv[1]] invalid_votes = [] for i in user_votes: if i not in vote_obj.options: invalid_votes.append(i) if not invalid_votes: votes[msg.channel][msg.user] = user_votes if already_voted: main.bot.send( msg.reply( f'@{get_user_name(msg)} Successfully overridden vote. ' f'New vote is {user_votes!r}')) else: return f'@{get_user_name(msg)} Successfully voted for {user_votes!r}' else: if len(invalid_votes) == 1: return f'@{get_user_name(msg)} Invalid option: {invalid_votes[0]!r}' else: return f'@{get_user_name(msg)} Invalid option(s): {invalid_votes!r}' if argv[1] in vote_obj.options: votes[msg.channel][msg.user] = argv[1] if already_voted: main.bot.send( msg.reply( f'@{get_user_name(msg)} Successfully overridden vote. ' f'New vote is {argv[1]!r}')) else: return f'@{get_user_name(msg)} Successfully voted for {argv[1]!r}' else: if ',' in argv[1]: return f'@{get_user_name(msg)} Cannot vote for multiple options in this poll.' else: return f'@{get_user_name(msg)} Invalid option: {argv[1]}'
async def c_hastebin(self, msg: twitchirc.ChannelMessage): cd_state = main.do_cooldown('hastebin', msg, global_cooldown=0, local_cooldown=30) if cd_state: return data = main.delete_spammer_chrs(msg.text).rstrip(' ').split(' ', 1)[1] link = await self.upload(data) return f'@{msg.user} Here\'s your hastebin link {self.hastebin_addr}{link}'
def c_cookie_optin(self, msg: twitchirc.ChannelMessage): cd_state = main.do_cooldown('cookie', msg, global_cooldown=60, local_cooldown=60) if cd_state: return if msg.user.lower() in self.cookie_optin: self.cookie_optin.remove(msg.user.lower()) return f'@{msg.user} You have been removed from the cookie opt-in list.' else: self.cookie_optin.append(msg.user.lower()) return f'@{msg.user} You have been added to the cookie opt-in list.'
def command_suggest(msg: twitchirc.ChannelMessage): cd_state = main.do_cooldown('suggest', msg, global_cooldown=0, local_cooldown=30) if cd_state: return t = main.delete_spammer_chrs(msg.text).rstrip(' ').split(' ', 1) if len(t) == 1: return f'@{msg.user}, Usage: suggest <text...>' s = Suggestion(author=main.User.get_by_message(msg), text=t[1], state=Suggestion.SuggestionState.new, notes='<no notes>') with main.session_scope() as session: session.add(s) return f'@{msg.user} Suggestion saved, hopefully. ID: {s.id}'
async def command_title(msg: twitchirc.ChannelMessage): cd_state = main.do_cooldown('title', msg, global_cooldown=30, local_cooldown=60) if cd_state: return async with aiohttp.request( 'get', 'https://api.twitch.tv/helix/streams', params={'user_login': msg.channel}, headers={'Client-ID': twitch_auth.json_data['client_id']}) as request: json_data = await request.json() if request.status == 200 and 'data' in json_data and len( json_data['data']) > 0: return f'@{msg.user} {json_data["data"][0]["title"]}' else: return f'@{msg.user} eShrug Stream not found.'
async def c_clip(self, msg: twitchirc.ChannelMessage): cd_state = main.do_cooldown(cmd='quick_clip', msg=msg, global_cooldown=30, local_cooldown=60) if cd_state: return try: clip_url = await self.create_clip(msg) except arg_parser.ParserError as e: return f'@{msg.user}, {e}' if clip_url == 'OFFLINE': return f'@{msg.user}, Cannot create a clip of an offline channel' else: rand = random.randint(0, 100) if rand == 69: return f'@{msg.user}, Cliped it LUL {clip_url}' else: return f'@{msg.user}, Created clip FeelsDankMan {chr(0x1f449)} {clip_url}'
def command_help(msg: main.StandardizedMessage): cd_state = main.do_cooldown('help', msg, global_cooldown=10, local_cooldown=20) if cd_state: return argv = shlex.split(msg.text.replace('\U000e0000', '')) args: typing.List[str] = argv[1:] if len(argv) else [] section = None if len(args): if args[0].isnumeric(): section = int(args[0]) args.pop(0) topic = ' '.join(args) # let the user quote the topic if topic == '' and section is None: # no arguments given topic = 'mb.help' elif topic == '': # only section given return f'@{msg.user} {find_topic("section_doc", section)}' if topic in [ 'all', 'topics', 'all topics', 'help topics', 'commands', 'all commands' ]: r = _command_help_check_special_topics(topic, msg) if isinstance(r, list): if len(r) > 3: return f'@{msg.user} Sorry, I can\'t send {len(r)} messages. WAYTOODANK' else: for m in r: main.bot.send(msg.reply(m)) elif r is None: raise RuntimeError( 'plugin_help._command_help_check_special_topics returned None') else: return f'@{msg.user} {find_topic(topic, section)}'
async def command_uptime(msg: twitchirc.ChannelMessage): cd_state = main.do_cooldown('uptime', msg, global_cooldown=30, local_cooldown=60) if cd_state: return async with aiohttp.request( 'get', 'https://api.twitch.tv/helix/streams', params={'user_login': msg.channel}, headers={'Client-ID': twitch_auth.json_data['client_id']}) as c_uptime_r: json_data = await c_uptime_r.json() print(json_data) data = json_data['data'] if data: data = data[0] start_time = datetime.datetime(*( time.strptime(data['started_at'], "%Y-%m-%dT%H:%M:%SZ")[0:6])) uptime = round_time_delta(datetime.datetime.utcnow() - start_time) return f'@{msg.user} {msg.channel} has been live for {uptime!s}' else: return f'@{msg.user} {msg.channel} is not live.'
async def command_whois(msg: twitchirc.ChannelMessage): if not bots or bots_invalidates <= time.time(): await _load_bots() cd_state = main.do_cooldown('whois', msg, global_cooldown=30, local_cooldown=60) if cd_state: return try: argv = shlex.split(msg.text.replace('\U000e0000', '')) except ValueError as e: return f'@{msg.user} FeelsWeirdMan {e.args}' args = whois_parser.parse_args(argv[1:] if len(argv) > 1 else []) if args is None: return f'@{msg.user} {whois_parser.format_usage()}' if args.id is not None: name = args.id id_ = True elif args.name is not None: name = args.name id_ = False else: return f'@{msg.user} Do you really want the bot to crash?' async with aiohttp.request( 'get', f'https://api.ivr.fi/twitch/resolve/{name}', params=({ 'id': 1 }) if id_ else {}, headers={ 'User-Agent': 'Mm\'sUtilityBot/v1.0 (by Mm2PL), Twitch chat bot', 'Requested-By': msg.user }) as request: data = await request.json() if data['status'] == 404: return f'@{msg.user} No such user found.' roles = '' if data['roles']['isAffiliate']: roles += 'affiliate, ' if data['roles']['isPartner']: roles += 'partner, ' if data['roles']['isSiteAdmin']: roles += 'site admin, ' if data['roles']['isStaff']: roles += 'staff, ' if roles == '': roles = 'none' roles = (roles[::-1].replace(', '[::-1], '', 1))[::-1] # replace last ", " with "". if data['displayName'].lower() != data['login'].lower(): login = f'({data["login"]})' else: login = '' created_on = datetime.datetime.strptime(data['createdAt'][:-8], '%Y-%m-%dT%H:%M:%S') bot_notes = '' bot = _find_bot(data['login']) if bot is not None: last_active = f', Last active: {bot["lastSeen"][:-4]}' if bot[ 'lastSeen'] is not None else '' 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}') return ( f'@{msg.user}, {"BANNED " if data["banned"] else ""}{"bot " if data["bot"] else ""}' f'user {data["displayName"]}{login}, ' f'chat color: {data["chatColor"]}, ' f'account created at {created_on}, ' f'roles: {roles}, ' f'id: {data["id"]}, ' f'bio: ' f'{data["bio"] if data["bio"] is not None else "<blank>"}' f'{bot_notes}')
async def command_downtime(msg: twitchirc.ChannelMessage): cd_state = main.do_cooldown('downtime', msg, global_cooldown=30, local_cooldown=60) if cd_state: return async with aiohttp.request( 'get', 'https://api.twitch.tv/helix/streams', params={'user_login': msg.channel}, headers={'Client-ID': twitch_auth.json_data['client_id']}) as uptime_req: json_data = await uptime_req.json() data = json_data['data'] if data: return f'@{msg.user} {msg.channel} is live.' async with aiohttp.request( 'get', 'https://api.twitch.tv/helix/users', params={'login': msg.channel}, headers={'Client-ID': twitch_auth.json_data['client_id']}) as user_req: # channel is not live json_data = await user_req.json() data = json_data['data'] user_id = data[0]['id'] with aiohttp.request( 'get', 'https://api.twitch.tv/helix/videos', params={ 'user_id': user_id, 'sort': 'time', 'type': 'archive', 'first': '1' }, headers={'Client-ID': twitch_auth.json_data['client_id']}) as video_r: json_data = await video_r.json() print(json_data) if not json_data['data']: return data = json_data['data'][0] duration = _parse_duration(data['duration']) print(duration) struct_time = time.strptime(data['created_at'], "%Y-%m-%dT%H:%M:%SZ") print(struct_time) created_at = datetime.datetime(year=struct_time[0], month=struct_time[1], day=struct_time[2], hour=struct_time[3], minute=struct_time[4], second=struct_time[5]) now = datetime.datetime.utcnow() time_start_difference = now - created_at offline_for = round_time_delta(time_start_difference - duration) print(duration, created_at, offline_for) return f'@{msg.user} {msg.channel} has been offline for {offline_for}'
async def c_braillefy(self, msg: twitchirc.ChannelMessage): cd_state = main.do_cooldown('braille', global_cooldown=0, local_cooldown=60, msg=msg) if cd_state: return try: args = arg_parser.parse_args(msg.text.split(' ', 1)[1], { 'url': str, 'sensitivity_r': float, 'sensitivity_g': float, 'sensitivity_b': float, 'sensitivity_a': float, 'size_percent': float, 'max_y': int, 'pad_y': int, 'reverse': bool, 'hastebin': bool }, strict_escapes=True, strict_quotes=True) except arg_parser.ParserError as e: return f'Error: {e.message}' missing_args = arg_parser.check_required_keys(args, ['url']) if missing_args: return ( f'Error: You are missing the {",".join(missing_args)} ' f'argument{"s" if len(missing_args) > 1 else ""} to run this command.' ) num_defined = sum( [args[f'sensitivity_{i}'] is not Ellipsis for i in 'rgb']) alpha = args['sensitivity_a'] if args[ 'sensitivity_a'] is not Ellipsis else 1 if num_defined == 3: sens: typing.Tuple[float, float, float, float] = (args['sensitivity_r'], args['sensitivity_g'], args['sensitivity_b'], alpha) is_zero = bool(sum([args[f'sensitivity_{i}'] == 0 for i in 'rgba'])) if is_zero: return f'Error: Sensitivity cannot be zero. MEGADANK' elif num_defined == 0: sens = (1, 1, 1, 1) else: return f'Error: you need to define either all sensitivity fields (r, g, b, a) or none.' if args['size_percent'] is not ... and args['max_y'] is not ...: return f'Error: you cannot provide the size percentage and maximum height at the same time.' max_x = 60 if args['size_percent'] is Ellipsis else None max_y = (args['max_y'] if args['max_y'] is not Ellipsis else 60) if args['size_percent'] is Ellipsis else None size_percent = None if args['size_percent'] is Ellipsis else args[ 'size_percent'] img = await braille.download_image(args['url']) img: Image.Image if img.format.lower() != 'gif': img, o = await braille.crop_and_pad_image( True, img, max_x, max_y, '', (60, args['pad_y'] if args['pad_y'] is not Ellipsis else 60), size_percent) o += await braille.to_braille_from_image( img, reverse=True if args['reverse'] is not Ellipsis else False, size_percent=size_percent, max_x=max_x, max_y=max_y, sensitivity=sens, enable_padding=True, pad_size=(60, args['pad_y'] if args['pad_y'] is not Ellipsis else 60), enable_processing=False) else: missing_permissions = main.bot.check_permissions( msg, ['cancer.braille.gif'], enable_local_bypass=False) if missing_permissions: o = 'Note: missing permissions to convert a gif. \n' else: o = '' frame = -1 start_time = time.time() while 1: try: img.seek(frame + 1) except EOFError: break frame += 1 o += f'\nFrame {frame}\n' frame_start = time.time() o += await braille.to_braille_from_image( img.copy(), reverse=True if args['reverse'] is not Ellipsis else False, size_percent=size_percent, max_x=max_x, max_y=max_y, sensitivity=sens, enable_padding=True, pad_size=(60, (args['pad_y'] if args['pad_y'] is not Ellipsis else 60)), enable_processing=True) time_taken = round(time.time() - start_time) frame_time = round(time.time() - frame_start) if frame % self.status_every_frames == 0 and time_taken > self.time_before_status: speed = round(1 / frame_time) main.bot.send( msg.reply( f'@{msg.user}, ppCircle Converted {frame} frames in ' f'{time_taken} seconds, speed: {round(speed)} fps, ' f'eta: {(img.n_frames - frame) * speed} seconds.' )) await asyncio.sleep(0) sendable = ' '.join(o.split('\n')[1:]) if args['hastebin'] is not Ellipsis or len(sendable) > 500: return ( f'{"This braille was too big to be posted." if not args["hastebin"] is not Ellipsis else ""} ' f'Here\'s a link to a hastebin: ' f'{plugin_hastebin.hastebin_addr}' f'{await plugin_hastebin.upload(o)}') else: return sendable