Esempio n. 1
0
    def format_set_channel_mode(self, settings: ChannelSettings,
                                old_settings=ChannelSettings()) -> typing.List:
        """
        Generate commands needed to change

        :param settings: Settings to apply.
        :param old_settings: Settings from before. Used to not execute commands that are not needed.
        :return: A list of ChannelMessages containing commands.
        """
        commands = []
        for num, val in enumerate(settings):
            if val != old_settings[num]:
                c_entry = COMMANDS[settings._fields[num]]
                if isinstance(settings[num], bool):
                    replacement = ''
                else:
                    replacement = str(settings[num])
                text = (c_entry[settings[num] is not False]
                        .replace('{}',
                                 replacement))
                msg = twitchirc.ChannelMessage(text=text,
                                               user='******', outgoing=True,
                                               channel=self.target_channel, parent=self.parent)
                commands.append(msg)
        return commands
Esempio n. 2
0
 def command_part(msg: twitchirc.ChannelMessage):
     p = twitchirc.ArgumentParser(prog='!part', add_help=False)
     p.add_argument('channel',
                    metavar='CHANNEL',
                    nargs='?',
                    const=msg.channel,
                    default=msg.channel)
     p.add_argument('-h', '--help', action='store_true', dest='help')
     args = p.parse_args(msg.text.split(' ')[1:])
     if args is None or args.help:
         usage = msg.reply(f'@{msg.user} {p.format_usage()}')
         bot.send(usage)
         return
     if args.channel == '':
         args.channel = msg.channel
     channel = args.channel.lower()
     if channel != msg.channel.lower() and bot.check_permissions(
             msg, [twitchirc.PERMISSION_COMMAND_PART_OTHER],
             enable_local_bypass=False):
         bot.send(
             msg.reply(
                 f"Cannot part from channel {channel}: your permissions are not valid in that channel."
             ))
         return
     if channel not in bot.channels_connected:
         bot.send(msg.reply(f'Not in {channel}'))
         return
     else:
         bot.send(msg.reply(f'Parting from {channel}'))
         m = twitchirc.ChannelMessage('Bye.', 'OUTGOING', channel)
         m.outgoing = True
         bot.send(m)
         bot.flush_single_queue(msg.channel)
         bot.part(channel)
Esempio n. 3
0
    def format_clear_channel(self):
        """
        Run the clear command on the targeted channel.

        :return: ChannelMessage containing generated command.
        """
        return twitchirc.ChannelMessage(user='******', text=f'/clear',
                                        channel=self.target_channel, outgoing=True)
Esempio n. 4
0
def create_msg(text, channel) -> twitchirc.ChannelMessage:
    """
    Create a ChannelMessage with the provided text and channel.

    :return: Newly created ChannelMessage object.
    """
    msg = twitchirc.ChannelMessage(text=text, channel=channel, user='******')
    msg.outgoing = True
    return msg
Esempio n. 5
0
    def format_delete(self):
        """
        Create a message with the /delete command in it.

        :return: ChannelMessage containing generated command.
        """
        if self.target_message_id is not None:
            return twitchirc.ChannelMessage(user='******', text=f'/delete {self.target_message_id}',
                                            channel=self.target_channel, outgoing=True)
        else:
            raise RuntimeError("This ModerationContainer doesn't target a message.")
def remind(reminder, channel):
    msg = twitchirc.ChannelMessage(
        user='******',
        channel=channel,
        text=
        f'@{reminder["user"]} As promised I\'m reminding you: {reminder["text"]}'
    )
    msg.outgoing = True
    main.bot.send(msg)
    if reminder['nr']:
        reminder['timestamp'] = time.time() + reminder['seconds']
Esempio n. 7
0
    def format_set_vip(self, status):
        """
        Create a message with the /[un]vip command in it.

        :param status: New status for the user. True for vip, False for unvip.
        :return: ChannelMessage containing generated command.
        """
        if self.target_user is not None:
            return twitchirc.ChannelMessage(user='******',
                                            text=f'/{"un" if not status else ""}vip {self.target_user}',
                                            channel=self.target_channel, outgoing=True)
        else:
            raise RuntimeError("This ModerationContainer doesn't target a user.")
Esempio n. 8
0
    def format_set_mod(self, status):
        """
        Run the [un]mod command on the targeted user.

        :param status: New status for the user. True for mod, False for unmod.
        :return: ChannelMessage containing generated command.
        """
        if self.target_user is not None:
            return twitchirc.ChannelMessage(user='******',
                                            text=f'/{"un" if not status else ""}mod {self.target_user}',
                                            channel=self.target_channel, outgoing=True)
        else:
            raise RuntimeError("This ModerationContainer doesn't target a user.")
Esempio n. 9
0
    def format_permaban(self, reason: typing.Optional[str] = None):
        """
        Create a message with the /ban command in it.

        :param reason: Reason for this action.
        :return: ChannelMessage containing generated command.
        """
        if self.target_user is not None:
            return twitchirc.ChannelMessage(user='******', text=f'/ban {self.target_user}'
                                                                  f'{f" {reason}" if reason is not None else ""}',
                                            channel=self.target_channel, outgoing=True)
        else:
            raise RuntimeError("This ModerationContainer doesn't target a user.")
Esempio n. 10
0
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}?'
Esempio n. 11
0
 def test_compose_whisper_via_reply_to_privmsg(self):
     orig = twitchirc.ChannelMessage('original', 'some_user',
                                     'some_channel')
     msg = orig.reply_directly('Here is some text')
     self.assertEqual('[email protected]',
                      msg.source)
     self.assertEqual('WHISPER', msg.action)
     self.assertEqual(True, msg.outgoing)
     self.assertEqual(None, msg.raw_data)
     self.assertEqual(':Here is some text', msg.args)
     self.assertEqual(['Here is some text'], msg.new_args)
     self.assertEqual(b'PRIVMSG #jtv :/w some_user Here is some text\r\n',
                      bytes(msg))
     self.assertEqual({}, msg.flags)
Esempio n. 12
0
    def test_compose_via_reply_to_privmsg(self):
        orig = twitchirc.ChannelMessage('original', 'some_user',
                                        'some_channel')
        msg = orig.reply('Here is some text')
        self.assertEqual('[email protected]',
                         msg.source)
        self.assertEqual('PRIVMSG', msg.action)
        self.assertEqual(True, msg.outgoing)
        self.assertEqual(None, msg.raw_data)
        self.assertEqual('#some_channel :Here is some text', msg.args)
        self.assertEqual(['#some_channel', 'Here is some text'], msg.new_args)
        self.assertEqual({}, msg.flags)

        orig = twitchirc.ChannelMessage('original', 'some_user',
                                        'some_channel')
        msg = orig.reply('/help')
        self.assertEqual('[email protected]',
                         msg.source)
        self.assertEqual('PRIVMSG', msg.action)
        self.assertEqual(True, msg.outgoing)
        self.assertEqual(None, msg.raw_data)
        self.assertEqual('#some_channel :/ /help', msg.args)
        self.assertEqual(['#some_channel', '/ /help'], msg.new_args)
        self.assertEqual({}, msg.flags)

        orig = twitchirc.ChannelMessage('original', 'some_user',
                                        'some_channel')
        msg = orig.reply('/help', True)
        self.assertEqual('[email protected]',
                         msg.source)
        self.assertEqual('PRIVMSG', msg.action)
        self.assertEqual(True, msg.outgoing)
        self.assertEqual(None, msg.raw_data)
        self.assertEqual('#some_channel :/help', msg.args)
        self.assertEqual(['#some_channel', '/help'], msg.new_args)
        self.assertEqual({}, msg.flags)
Esempio n. 13
0
    def format_timeout(self, time: typing.Union[int, str], reason: typing.Optional[str] = None):
        """
        Create a message with the /timeout command in it.

        :param time: Time to block the user for.
        :param reason: Reason for this action.
        :return: ChannelMessage containing generated command.
        """
        if isinstance(time, str) and ((not time[-1].isalpha()) or time.isalpha()):
            raise ValueError('Time needs to be an int or string with the last character indicating the unit.')
        if self.target_user is not None:
            return twitchirc.ChannelMessage(user='******', text=f'/timeout {self.target_user} {time}'
                                                                  f'{f" {reason}" if reason is not None else ""}',
                                            channel=self.target_channel, outgoing=True)
        else:
            raise RuntimeError("This ModerationContainer doesn't target a user.")
Esempio n. 14
0
def _acommand_error_handler(exception, command, message):
    msg = twitchirc.ChannelMessage(
        text=f'Errors monkaS {chr(128073)} ALERT: {exception!r}',
        user='******',
        channel=error_notification_channel)
    msg.outgoing = True
    main.bot.force_send(msg)
    log('err', f'Error while running command {command.chat_command}')
    log('info', f'{message.user}@{message.channel}: {message.text}')
    for i in traceback.format_exc(30).split('\n'):
        log('err', i)

    msg2 = message.reply(
        f'@{message.user}, an error was raised during the execution of your command: '
        f'{command.chat_command}')
    main.bot.send(msg2)
Esempio n. 15
0
def _command_run(sock: socket.socket, msg: str, socket_id):
    text = msg.split(' ', 1)[1]
    message = twitchirc.ChannelMessage(channel=f'__IPC {socket_id}',
                                       text=text,
                                       user=f'__IPC {socket_id}')
    message.flags = {
        'badge-info': '',
        'badges': ['broadcaster/1'],
        'color': '#ffffff',
        'display-name': f'__IPC_Socket_id_{socket_id}',
        'id': str(uuid.uuid4()),
        'room-id': socket_id + 1000,
        'user-id': socket_id + 1000,
        'turbo': '0',
        'subscriber': '0',
        'emotes': ''
    }
    # noinspection PyProtectedMember
    main.bot._call_command_handlers(message)
    return f'~Sent: {text} as {message.user!r}\r\n'.encode('utf-8')
Esempio n. 16
0
 def fire(self, event: Event) -> None:
     print('FIRE!!!!! WAYTOODANK')
     if self.fire_triggered:
         return
     self.fire_triggered = True
     pinged = ''
     if isinstance(event.source, twitchirc.Bot):
         for user, perms in event.source.permissions.users.items():
             print(user, perms)
             if ((twitchirc.permission_names.GLOBAL_BYPASS_PERMISSION
                  in perms or 'util.fire_ping' in perms
                  or 'parent.bot_admin' in perms)
                     and 'util.fire_ping_disable' not in perms):
                 pinged += f' @{user}'
     e = event.data['exception']
     m = twitchirc.ChannelMessage(
         text=f'The bot\'s breaking!!! {pinged} WAYTOODANK {e!r}',
         user='******',
         channel=error_notification_channel,
         outgoing=True,
         parent=None)
     event.source.send(m)
     event.source.flush_queue()
Esempio n. 17
0
 def run_commands_from_file(self, file_object):
     lines = file_object.readlines()
     user = '******'
     channel = 'rcfile'
     self._in_rc_mode = True
     for num, i in enumerate(lines):
         i: str = i.replace('\n', '')
         if i.startswith('@'):
             if i.startswith('@at'):
                 channel = i.replace('@at ', '')
             elif i.startswith('@as'):
                 user = i.replace('@as ', '')
             continue
         m = twitchirc.ChannelMessage(user=user, channel=channel, text=i)
         m.flags = {
             'badge-info': '',
             'badges': 'moderator/1',
             'display-name': 'RCFile',
             'id': '00000000-0000-0000-0000-{:0>12}'.format(num),
             'user-id': 'rcfile',
             'emotes': ''
         }
         self._call_command_handlers(m)
     self._in_rc_mode = False
Esempio n. 18
0
 def test_compose_privmsg(self):
     msg = twitchirc.ChannelMessage('Testing123 456',
                                    'OUTGOING',
                                    'test',
                                    outgoing=True)
     self.assertEqual(b'PRIVMSG #test :Testing123 456\r\n', bytes(msg))
Esempio n. 19
0
    subs = bot.storage['subs']
bot.twitch_mode()

bot.join(bot.username.lower())

if 'channels' in bot.storage.data:
    for i in bot.storage['channels']:
        if i in bot.channels_connected:
            print(f'Skipping joining channel: {i}: Already connected.')
            continue
        bot.join(i)

if prog_args.restart_from:
    msg = twitchirc.ChannelMessage(
        user='******',
        channel=prog_args.restart_from[1],
        text=(f'@{prog_args.restart_from[0]} Restart OK. (debug)'
              if prog_args.debug else
              f'@{prog_args.restart_from[0]} Restart OK.'))
    msg.outgoing = True

    def _send_restart_message(*a):
        del a
        bot.send(msg, queue='misc')
        bot.flush_queue(1000)

    bot.schedule_event(1, 15, _send_restart_message, (), {})
try:
    bot.run()
except BaseException as e:
    traceback.print_exc()
    bot.call_middleware('fire', {'exception': e}, False)
Esempio n. 20
0
async def main():
    global pubsub
    languages.load_data()
    auth = {
        Platform.TWITCH:
        (bot.storage['self_twitch_name'],
         'oauth:' + util_bot.twitch_auth.json_data['access_token'])
    }
    wait = False
    for plat in Platform:
        if plat in auth:
            continue
        if plat.name.casefold() in util_bot.other_platform_auth:
            auth[plat] = util_bot.other_platform_auth[plat.name.casefold()]
        else:
            log(
                'warn',
                f'Failed to load auth for {plat.name} from auth file. Key: {plat.name.casefold()!r}'
            )
            wait = True
    if not auth:
        log('err', 'No platform authentication found')
        log('err', 'Exiting')
        return

    if wait:
        print('Please wait 5 seconds')
        time.sleep(5)

    await bot.init_clients(auth)
    bot.clients[Platform.TWITCH].connection.middleware.append(
        UserStateCapturingMiddleware())
    bot.username = bot.storage['self_twitch_name']

    if 'self_id' in bot.storage.data:
        uid = bot.storage['self_id']
    else:
        try:
            uid = util_bot.twitch_auth.new_api.get_users(
                login=bot.username.lower())[0].json()['data'][0]['id']
        except KeyError:
            util_bot.twitch_auth.refresh()
            util_bot.twitch_auth.save()
            uid = util_bot.twitch_auth.new_api.get_users(
                login=bot.username.lower())[0].json()['data'][0]['id']
        bot.storage['self_id'] = uid

    await init_server(grpc_listen_addresses)
    await bot.aconnect()
    bot.cap_reqs(False)

    for i in bot.storage['channels']:
        if i in bot.channels_connected:
            log('info', f'Skipping joining channel: {i}: Already connected.')
            continue
        await bot.join(i)

    if prog_args.restart_from:
        msg = twitchirc.ChannelMessage(
            user='******',
            channel=prog_args.restart_from[1],
            text=(f'@{prog_args.restart_from[0]} Restart OK. (debug)'
                  if prog_args.debug else
                  f'@{prog_args.restart_from[0]} Restart OK.'))
        msg.outgoing = True

        def _send_restart_message(*a):
            del a
            bot.send(msg)
            bot.flush_queue(1000)

        bot.schedule_event(1, 15, _send_restart_message, (), {})

    pubsub = await init_pubsub(util_bot.twitch_auth.new_api.auth.token)

    temp = util_bot.make_log_function('pubsub')
    pubsub.log_function = lambda *text, **kwargs: temp('debug', *text, **kwargs
                                                       )

    pubsub.listen([f'chat_moderator_actions.{uid}.{uid}'])
    bot.pubsub = pubsub
    bot.middleware.append(pubsub_middleware)
    for i in bot.channels_connected:
        pubsub_middleware.join(
            twitchirc.Event('join', {'channel': i},
                            bot,
                            cancelable=False,
                            has_result=False))

    await bot.join(bot.username.lower())

    futures = []
    log('info', 'Spam async inits')
    for plugin in util_bot.plugins.values():
        log('info', f'Call async init for {plugin}')
        futures.append(asyncio.create_task(plugin.async_init()))

    async_init_time = 5.0

    _, pending = await asyncio.wait(futures,
                                    timeout=async_init_time,
                                    return_when=asyncio.ALL_COMPLETED)
    # wait for every plugin to initialize before proceeding
    if pending:
        log(
            'err',
            f'Waiting for async inits timed out after {async_init_time} seconds. '
            f'Assuming waiting longer will not help, exiting.')

        plugin_objects = list(util_bot.plugins.values())
        table_data = [('State', 'Plugin name', 'Path')]
        for index, fut in enumerate(futures):
            fut: asyncio.Future
            plugin = plugin_objects[index]  # order should be the same.
            done = 'done' if fut.done() else 'TIMED OUT'

            table_data.append((done, plugin.name, plugin.source))
        table = ''
        cols = 3

        # calculate maximum length of elements for each row
        col_max = [
            len(max(table_data, key=lambda o: len(o[col_id]))[col_id])
            for col_id in range(cols)
        ]

        for row in table_data:
            for col_id, col in enumerate(row):
                table += col
                _print(col_max[col_id], repr(col), len(col))
                table += (col_max[col_id] - len(col) + 1) * ' '
            table += '\n'
        _print(table)
        log('warn', table)

        raise TimeoutError('Waiting for async inits timed out')
    log('warn', 'async inits done')
    try:
        done, pending = await asyncio.wait({bot.arun(), pubsub.task},
                                           return_when=asyncio.FIRST_COMPLETED)
    except KeyboardInterrupt:
        await bot.stop()
        return
    for j in done:
        await j  # retrieve any exceptions
Esempio n. 21
0
    async def nuke(self,
                   args,
                   msg,
                   users,
                   force_nuke=False,
                   reasons=None,
                   disable_hastebinning=False,
                   show_nuked_by=False):
        if reasons is None:
            reasons = {}

        if msg.user in users:
            users.remove(
                msg.user)  # make the executor not get hit by the fallout.
        if main.bot.username.lower() in users:
            users.remove(main.bot.username.lower())

        for u in users.copy():
            m = twitchirc.ChannelMessage(text='',
                                         user=u,
                                         channel=msg.channel,
                                         parent=main.bot)
            missing_permissions = main.bot.check_permissions(
                m, ['util.nuke.no_fallout'], enable_local_bypass=False)
            if not missing_permissions:
                users.remove(u)
        if disable_hastebinning:
            url = '(disabled)'
        else:
            url = plugin_hastebin.hastebin_addr + await plugin_hastebin.upload(
                "\n".join(users))
        if len(users) > self.max_nuke and not force_nuke:
            return (
                f'@{msg.user}, {"(dry run)" if args["dry-run"] else ""}'
                f'refusing to nuke {len(users)} users. Add the +force flag or force nuke the list.'
                f'Full list here: {url}')
        if args['dry-run'] is True:
            return (
                f'@{msg.user}, (dry run) {"timing out" if not args["perma"] else "banning (!!)"} {len(users)} '
                f'users. Full list here: '
                f'{url}')
        timeouts = []
        if not args['perma']:
            t_o_length = int(args['timeout'].total_seconds())
            for u in users:
                if show_nuked_by:
                    reason = f'nuked by {msg.user} - '
                else:
                    reason = ''
                if u in reasons:
                    reason += reasons[u]
                timeouts.append(
                    msg.reply(f'/timeout {u} {t_o_length}s {reason}',
                              force_slash=True))
        else:
            for u in users:
                if show_nuked_by:
                    reason = f'nuked by {msg.user} - '
                else:
                    reason = ''
                if u in reasons:
                    reason += reasons[u]
                timeouts.append(
                    msg.reply(f'/ban {u} {reason}', force_slash=True))

        number_of_needed_connections = self._calculate_number_of_connections(
            timeouts)
        main.bot.send(
            msg.reply_directly(
                self._make_nuke_notification_whisper(
                    args, msg, number_of_needed_connections, timeouts, users)))
        main.bot.flush_queue(1)
        num_of_timeouts = len(timeouts)
        db_log_level_bkup = plugin_logs.db_log_level
        log_level_bkup = plugin_logs.log_level
        try:
            if plugin_logs:
                plugin_logs.db_log_level = plugin_logs.log_levels[
                    'warn']  # don't spam the logs
                plugin_logs.log_level = plugin_logs.log_levels['warn']
            # time_taken = await self._send_using_multiple_connections(msg, timeouts, msg.channel,
            #                                                          number_of_needed_connections)
            time_taken = await self._send_using_new_connection_thread(
                msg, timeouts, msg.channel, number_of_needed_connections)
        finally:
            plugin_logs.db_log_level = db_log_level_bkup
            plugin_logs.log_level = log_level_bkup
        return self._make_nuke_end_message(msg, args, users, url, time_taken,
                                           timeouts)