Пример #1
0
    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 :)'
Пример #2
0
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])}')
Пример #3
0
 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))
Пример #4
0
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()}')
Пример #5
0
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]}'
Пример #6
0
    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}'
Пример #7
0
 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.'
Пример #8
0
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}'
Пример #9
0
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.'
Пример #10
0
    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}'
Пример #11
0
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)}'
Пример #12
0
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.'
Пример #13
0
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}')
Пример #14
0
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}'
Пример #15
0
    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