<commit_before>"""Interact with Beam chat servers."""

from logging import getLogger

from asyncio import sleep, ensure_future

from itertools import count, cycle

from json import loads, dumps

from aiohttp import ClientSession
from websockets.exceptions import ConnectionClosed

class BeamChat(ClientSession):
    """Interact with Beam chat."""

    def __init__(self, channel, *endpoints):
        super().__init__()

        self.logger = getLogger(__name__)

        assert isinstance(channel, int), "Channel ID must be an integer."
        self.channel = channel

        self.websocket = None

        self._packet_counter = count()
        self._endpoint_cycle = cycle(endpoints)

    async def connect(self, *auth, backoff=2):
        """Connect to a chat server."""
        _backoff_count = count()
        while True:
            try:
                self.websocket = await super().ws_connect(self._endpoint)
            except ConnectionRefusedError:
                seconds = min(backoff**next(_backoff_count), 60)
                self.logger.debug("Retrying in %s seconds...", seconds)
                await sleep(seconds)
            else:
                await self.authenticate(*auth)
                return self.websocket

    async def authenticate(self, *auth):
        """Send an authentication packet to chat."""

        await self.send(self.channel, *auth, method="auth", id="auth")
        return True

    async def send(self, *args, **kwargs):
        """Send a packet to chat."""

        if self.websocket is None:
            raise ConnectionError("Not connected. Run connect() first!")

        packet = {
            "type": "method",
            "method": "msg",
            "arguments": args,
            "id": kwargs.get("id") or self._packet_id
        }

        packet.update(kwargs)

        self.logger.debug(packet)

        self.websocket.send_str(dumps(packet))

    async def read(self, handle=None):
        """Read and parse packets from chat."""

        if self.websocket is None:
            raise ConnectionError("Not connected. Run connect() first!")

        while True:

            try:
                response = await self.websocket.receive()
            except ConnectionClosed:
                self.logger.warning("Connection to chat server lost. "
                                    "Attempting to reconnect.")
                await self.connect()
            else:
                packet = loads(response.data)

                if packet.get("error") is not None:
                    self.logger.error(packet)
                else:
                    self.logger.debug(packet)

                if callable(handle):
                    ensure_future(handle(packet))

    @property
    def _packet_id(self):
        return next(self._packet_counter)

    @property
    def _endpoint(self):
        return next(self._endpoint_cycle)
<commit_msg>Add aesthetic spacing<commit_after>"""Interact with Beam chat."""

from logging import getLogger

import asyncio

from itertools import count, cycle

import json

from aiohttp import ClientSession
from aiohttp.errors import ClientOSError, ServerDisconnectedError

class BeamChat(ClientSession):
    """Interact with Beam chat."""

    def __init__(self, channel, *endpoints):
        super().__init__()

        self.logger = getLogger(__name__)

        assert isinstance(channel, int), "Channel ID must be an integer."
        self.channel = channel

        assert len(endpoints), "An endpoint is required to connect."

        self.websocket = None

        self._packet_counter = count()
        self._endpoint_cycle = cycle(endpoints)

    async def connect(self, *auth, backoff=2):
        """Connect to a chat server."""

        _backoff_count = count()

        while True:
            try:
                self.websocket = await super().ws_connect(self._endpoint)
            except ClientOSError:
                seconds = min(backoff**next(_backoff_count), 60)
                self.logger.debug("Retrying in %s seconds...", seconds)
                await asyncio.sleep(seconds)
            else:
                await self.authenticate(*auth)
                self.logger.info("Connection established.")
                return self.websocket

    async def send(self, *args, **kwargs):
        """Send a packet to chat."""

        if self.websocket is None:
            raise ConnectionError("Not connected. Run connect() first!")

        packet = {
            "type": "method",
            "method": "msg",
            "arguments": args,
            "id": kwargs.get("id") or self._packet_id
        }

        packet.update(kwargs)

        self.logger.debug(packet)

        self.websocket.send_str(json.dumps(packet))

    async def read(self, handle=None):
        """Read and parse packets from chat."""

        if self.websocket is None:
            raise ConnectionError("Not connected. Run connect() first!")

        while True:
            response = (await self.websocket.receive()).data

            if isinstance(response, ServerDisconnectedError):
                self.logger.warning("Connection to chat server lost. "
                                    "Attempting to reconnect.")
                await self.connect()
            else:
                packet = json.loads(response)

                if packet.get("error") is not None:
                    self.logger.error(packet)
                else:
                    self.logger.debug(packet)

                if callable(handle):
                    asyncio.ensure_future(handle(packet))

    async def authenticate(self, *auth):
        """Send an authentication packet to chat."""

        await self.send(self.channel, *auth, method="auth", id="auth")
        return True

    @property
    def _packet_id(self):
        return next(self._packet_counter)

    @property
    def _endpoint(self):
        return next(self._endpoint_cycle)
<|endoftext|>


<commit_before># !python2
import math
import elo
class Player(object):

    def __init__(name='player',elo=1200.0,wins=0,losses=0,draws=0):
        self.name = name
        self.elo = elo
        self.wins = wins
        self.losses = losses
        self.draws = draws

    def total_games(self):
        return self.wins+self.losses+self.draws

    def __eq__(self,other):
        return self.elo == other.elo

    def __ne__(self,other):
        return self.elo != other.elo

    def __lt__(self,other):
        return self.elo < other.elo

    def __gt__(self,other):
        return self.elo > other.elo

    def __le__(self,other):
        return self.elo <= other.elo

    def __ge__(self,other):
        return self.elo >= other.elo

    def __int__(self):
        return math.ceil(self.elo)

<commit_msg>Delete player.py<commit_after><|endoftext|>


<commit_before>from collections import OrderedDict
from datetime import datetime, timezone
import logging
import ssl
import time

from . import log
from . import plugin

import irc.bot

irc.client.ServerConnection.buffer_class.errors = 'replace'

class Bot(irc.bot.SingleServerIRCBot):
    internal_commands = {
        'join': 'join a channel',
        'quit': 'force the bot to quit',
        'reload': 'force bot to reload all plugins',
        'enable': 'enable a plugin',
        'disable': 'disable a plugin',
        'op': 'add a user as bot operator',
        'deop': 'remove a user as bot operator',
        'ops': 'list all ops',
        'ban': 'ban a user from using the bot',
        'unban': 'remove bot ban for user',
        'banlist': 'currently banned nicks'
    }

    def __init__(self, channels, nickname, server, **kwargs):
        self.port = kwargs.get('port', 6667)
        self.ops = kwargs.get('ops', [])
        self.plugin_dir = kwargs.get('plugin_dir', 'plugins')
        self.ssl_required = kwargs.get('ssl_required', False)
        self.ns_pass = kwargs.get('ns_pass', None)
        self.nickserv = kwargs.get('nickserv', 'NickServ')
        self.log_level = kwargs.get('log_level', 'info')
        self.log_file = kwargs.get('log_file', None)
        self.server_pass = kwargs.get('server_pass', None)
        self.cmd_prefix = kwargs.get('cmd_prefix', '!')
        self.use_prefix_for_plugins = kwargs.get('use_prefix_for_plugins', False)
        self.disable_help = kwargs.get('disable_help', False)
        self.banned_users = kwargs.get('banned_users', [])
        if self.ssl_required:
            factory = irc.connection.Factory(wrapper=ssl.wrap_socket)
            irc.bot.SingleServerIRCBot.__init__(self, [(server, self.port, self.server_pass)], nickname, nickname, connect_factory=factory)
        else:
            irc.bot.SingleServerIRCBot.__init__(self, [(server, self.port, self.server_pass)], nickname, nickname)
        self.chanlist = channels
        self.bot_nick = nickname
        self.start_logging()
        self.internal_commands = {self.cmd_prefix + k: v for k,v in self.internal_commands.items()}
        plugin.load_plugins(self.plugin_dir, use_prefix=self.use_prefix_for_plugins, cmd_prefix=self.cmd_prefix)

    class Message:
        def __init__(self, bot, channel, nick, botnick, ops, logger, action, privmsg, notice, msg_type, cmd=None, arg=None, text=None, nick_list=None):
            self.bot = bot
            self.datetime = datetime.now(timezone.utc)
            self.timestamp = self.datetime.timestamp()
            self.channel = channel
            self.nick = nick
            self.nick_list = nick_list
            self.botnick = botnick
            self.ops = ops
            self.logger = logger
            self.action = action
            self.privmsg = privmsg
            self.notice = notice
            self.msg_type = msg_type
            if cmd:
                self.cmd = cmd
                self.arg = arg
            if text:
                self.text = text
            if not (cmd==None or text==None):
                raise TypeError('missing cmd or text parameter')

    def start_logging(self):
        self.logger = log.logger
        if self.log_file:
            log.set_log_file(self.log_file)
        else:
            log.set_log_file('{}.log'.format(self.bot_nick))
        if self.log_level == 'error':
            level = logging.ERROR
        elif self.log_level == 'warning':
            level = logging.WARNING
        elif self.log_level == 'info':
            level = logging.INFO
        elif self.log_level == 'debug':
            level = logging.DEBUG
        # Set levels
        if self.log_level != "off":
            self.logger.setLevel(level)
        self.logger.info('Logging started!')

    def on_welcome(self, c, e):
        if self.ns_pass:
            self.logger.info('identifying with nickserv')
            c.privmsg(self.nickserv, 'identify {}'.format(self.ns_pass))
        for channel in self.chanlist:
            self.logger.info('joining channel {}'.format(channel.split()[0]))
            c.join(*channel.split())

    def on_pubmsg(self, c, e):
        self.process_event(c, e)

    def on_privmsg(self, c, e):
        self.process_event(c, e)

    def on_action(self, c, e):
        self.process_event(c, e)

    def call_help(self, nick, op):
        cmds = {k:v.help_text for k,v in plugin.cmds.items() if not plugin.cmds[k].ops}
        cmds.update({self.cmd_prefix + 'help': 'returns this output to private message'})
        if op:
            cmds.update({k:v.help_text for k,v in plugin.cmds.items() if plugin.cmds[k].ops})
            cmds.update({k:v for k,v in self.internal_commands.items()})
        helpout = OrderedDict(sorted(cmds.items()))
        for h in helpout:
            self.connection.privmsg(nick, '{} -- {}'.format(h, helpout[h]))
            time.sleep(.1)
        self.connection.privmsg(nick, 'List of listeners: {}'.format(', '.join([l for l in plugin.lstnrs])))
        return None

    def call_internal_commands(self, channel, nick, cmd, text, arg, c):
        if not cmd.startswith(self.cmd_prefix):
            return None
        else:
            cmd = cmd[len(self.cmd_prefix):]
        output = None
        if nick in self.ops:
            op = True
        else:
            op = False
        if cmd == 'join' and op:
            try:
                c.join(*arg.split())
                self.logger.info('joining {} per request of {}'.format(arg, nick))
                output = plugin.message('{}: joined {}'.format(nick, arg.split()[0]))
            except:
                self.logger.exception('issue with join command: {}join #channel <channel key>'.format(self.cmd_prefix))
        elif cmd == 'quit' and op:
            self.logger.info('quitting per request of {}'.format(nick))
            if not arg:
                arg = "See y'all later!"
            c.quit(arg)
            quit()
        elif cmd == 'help' and not self.disable_help:
            self.call_help(nick, op)
        elif cmd == 'reload' and op:
            self.logger.info('reloading plugins per request of {}'.format(nick))
            plugin.load_plugins(self.plugin_dir, use_prefix=self.use_prefix_for_plugins, cmd_prefix=self.cmd_prefix)
            output = plugin.message('Plugins reloaded')
        elif cmd == 'enable' and op:
            if arg in plugin.plugins:
                if plugin.plugins[arg].enabled:
                    output = plugin.message("{}: '{}' already enabled".format(nick, arg))
                else:
                    plugin.plugins[arg].enable()
                    output = plugin.message("{}: '{}' enabled!".format(nick, arg))
            else:
                output = plugin.message("{}: '{}' not found".format(nick, arg))
        elif cmd == 'disable' and op:
            if arg in plugin.plugins:
                if not plugin.plugins[arg].enabled:
                    output = plugin.message("{}: '{}' already disabled".format(nick, arg))
                else:
                    plugin.plugins[arg].disable()
                    output = plugin.message("{}: '{}' disabled!".format(nick, arg))
        elif cmd == 'op' and op:
            for o in arg.split(' '):
                self.ops.append(o)
            output = plugin.message('{}: {} added as op'.format(nick, arg))
        elif cmd == 'deop' and op:
            for o in arg.split(' '):
                self.ops = [i for i in self.ops if i != o]
            output = plugin.message('{}: {} removed as op'.format(nick, arg))
        elif cmd == 'ops' and op:
            output = plugin.message('current ops: {}'.format(', '.join(self.ops)))
        elif cmd == 'ban' and op:
            for o in arg.split(' '):
                self.banned_users.append(o)
            output = plugin.message('{}: banned {}'.format(nick, arg))
        elif cmd == 'unban' and op:
            for o in arg.split(' '):
                self.banned_users = [i for i in self.banned_users if i != o]
            output = plugin.message('{}: removed ban for {}'.format(nick, arg))
        elif cmd == 'banlist':
            output = plugin.message('currently banned: {}'.format(', '.join(self.banned_users)))
        return output

    def call_plugins(self, privmsg, action, notice, chan, cmd, text, nick_list, nick, arg, msg_type):
        output = None
        if cmd in plugin.cmds:
            try:
                if plugin.cmds[cmd].ops and nick not in self.ops:
                    if plugin.cmds[cmd].ops_msg:
                        output =  plugin.message(plugin.cmds[cmd].ops_msg)
                elif plugin.cmds[cmd].enabled:
                    self.logger.debug('executing {}'.format(cmd))
                    output = plugin.cmds[cmd].run(self.Message(
                        bot=self,
                        channel=chan,
                        cmd=cmd,
                        nick_list=nick_list,
                        nick=nick,
                        arg=arg,
                        privmsg=privmsg,
                        action=action,
                        notice=notice,
                        botnick=self.bot_nick,
                        ops=self.ops,
                        logger=self.logger,
                        msg_type=msg_type
                    ))
            except Exception:
                self.logger.exception('issue with command {}'.format(cmd))
        else:
            for lstnr in plugin.lstnrs:
                if plugin.lstnrs[lstnr].enabled:
                    try:
                        self.logger.debug('whispering to listener: {}'.format(lstnr))
                        listen_output = plugin.lstnrs[lstnr].run(self.Message(
                            bot=self,
                            channel=chan,
                            text=text,
                            nick_list=nick_list,
                            nick=nick,
                            privmsg=privmsg,
                            action=action,
                            notice=notice,
                            botnick=self.bot_nick,
                            ops=self.ops,
                            logger=self.logger,
                            msg_type=msg_type
                        ))
                        if listen_output:
                            output = listen_output
                    except Exception:
                        self.logger.exception('issue with listener {}'.format(lstnr))
        if output:
            self.logger.debug(f'returning output: {output.msg}')
        return output

    def process_event(self, c, e):
        nick = e.source.nick
        if nick == self.bot_nick:
            pass
        if e.arguments:
            text = e.arguments[0]
        else:
            text = ''
        if e.type == 'privmsg' or e.type == 'pubmsg':
            msg_type = 'message'
        else:
            msg_type = e.type
        if e.target == self.bot_nick:
            chan = nick
            nick_list = [nick]
        else:
            chan = e.target
            nick_list = list(self.channels[chan].users())
        if e.type == 'action':
            cmd = ''
        else:
            cmd = text.split(' ')[0]
        self.logger.debug(
            'Message info: channel: {}, nick: {}, cmd: {}, text: {}'.format(chan, nick, cmd, text)
        )
        if len(text.split(' ')) > 1:
            arg = ''.join([i + ' ' for i in text.split(' ')[1:]]).strip()
        else:
            arg = ''
        output = self.call_internal_commands(chan, nick, cmd, text, arg, c)
        if not output:
            plugin_info = {
                'chan': chan,
                'cmd': cmd,
                'text': text,
                'nick_list': nick_list,
                'nick': nick,
                'arg': arg,
                'privmsg': c.privmsg,
                'action': c.action,
                'notice': c.notice,
                'msg_type': msg_type
            }
            output = self.call_plugins(**plugin_info)
        if output:
            self.logger.debug(f'sending output: {output.msg}')
            self.process_output(c, chan, output)

    def process_output(self, c, chan, output):
        if not output.msg:
            return
        for msg in output.msg:
            if output.msg_type == plugin.OutputType.Message:
                self.logger.debug('output message: {}'.format(msg))
                try:
                    c.privmsg(chan, msg)
                except irc.client.MessageTooLong:
                    self.logger.error('output message too long: {}'.format(msg))
                    break
            elif output.msg_type == plugin.OutputType.Action:
                self.logger.debug('output action: {}'.format(msg))
                try:
                    c.action(chan, msg)
                except irc.client.MessageTooLong:
                    self.logger.error('output message too long: {}'.format(msg))
                    break
            else:
                self.logger.warning("Unsupported output type '{}'".format(output.msg_type))
            time.sleep(.5)

class TwitchBot(Bot):
    def __init__(self, nickname, channel, token, **kwargs):
        self.port = kwargs.get('port', 6667)
        self.ops = kwargs.get('ops', [])
        self.plugin_dir = kwargs.get('plugin_dir', 'plugins')
        self.log_level = kwargs.get('log_level', 'info')
        self.log_file = kwargs.get('log_file', None)
        self.server_pass = kwargs.get('server_pass', None)
        self.cmd_prefix = kwargs.get('cmd_prefix', '!')
        self.use_prefix_for_plugins = kwargs.get('use_prefix_for_plugins', False)
        self.disable_help = kwargs.get('disable_help', False)
        self.banned_users = kwargs.get('banned_users', [])
        self.bot_nick = nickname
        self.start_logging()
        self.channel = channel
        self.logger.info('Joining Twitch Server')
        irc.bot.SingleServerIRCBot.__init__(self, [('irc.twitch.tv', 6667, 'oauth:'+token)], nickname, nickname)
        self.internal_commands = {self.cmd_prefix + k: v for k,v in self.internal_commands.items()}
        plugin.load_plugins(self.plugin_dir, use_prefix=self.use_prefix_for_plugins, cmd_prefix=self.cmd_prefix)

    def on_welcome(self, c, e):
        self.logger.info('requesting permissions')
        c.cap('REQ', ':twitch.tv/membership')
        c.cap('REQ', ':twitch.tv/tags')
        c.cap('REQ', ':twitch.tv/commands')
        self.logger.info('Joining channel ' + self.channel)
        c.join(self.channel)
<commit_msg>fix for listeners<commit_after>from collections import OrderedDict
from datetime import datetime, timezone
import logging
import ssl
import time

from . import log
from . import plugin

import irc.bot

irc.client.ServerConnection.buffer_class.errors = 'replace'

class Bot(irc.bot.SingleServerIRCBot):
    internal_commands = {
        'join': 'join a channel',
        'quit': 'force the bot to quit',
        'reload': 'force bot to reload all plugins',
        'enable': 'enable a plugin',
        'disable': 'disable a plugin',
        'op': 'add a user as bot operator',
        'deop': 'remove a user as bot operator',
        'ops': 'list all ops',
        'ban': 'ban a user from using the bot',
        'unban': 'remove bot ban for user',
        'banlist': 'currently banned nicks'
    }

    def __init__(self, channels, nickname, server, **kwargs):
        self.port = kwargs.get('port', 6667)
        self.ops = kwargs.get('ops', [])
        self.plugin_dir = kwargs.get('plugin_dir', 'plugins')
        self.ssl_required = kwargs.get('ssl_required', False)
        self.ns_pass = kwargs.get('ns_pass', None)
        self.nickserv = kwargs.get('nickserv', 'NickServ')
        self.log_level = kwargs.get('log_level', 'info')
        self.log_file = kwargs.get('log_file', None)
        self.server_pass = kwargs.get('server_pass', None)
        self.cmd_prefix = kwargs.get('cmd_prefix', '!')
        self.use_prefix_for_plugins = kwargs.get('use_prefix_for_plugins', False)
        self.disable_help = kwargs.get('disable_help', False)
        self.banned_users = kwargs.get('banned_users', [])
        if self.ssl_required:
            factory = irc.connection.Factory(wrapper=ssl.wrap_socket)
            irc.bot.SingleServerIRCBot.__init__(self, [(server, self.port, self.server_pass)], nickname, nickname, connect_factory=factory)
        else:
            irc.bot.SingleServerIRCBot.__init__(self, [(server, self.port, self.server_pass)], nickname, nickname)
        self.chanlist = channels
        self.bot_nick = nickname
        self.start_logging()
        self.internal_commands = {self.cmd_prefix + k: v for k,v in self.internal_commands.items()}
        plugin.load_plugins(self.plugin_dir, use_prefix=self.use_prefix_for_plugins, cmd_prefix=self.cmd_prefix)

    class Message:
        def __init__(self, bot, channel, nick, botnick, ops, logger, action, privmsg, notice, msg_type, cmd=None, arg=None, text=None, nick_list=None):
            self.bot = bot
            self.datetime = datetime.now(timezone.utc)
            self.timestamp = self.datetime.timestamp()
            self.channel = channel
            self.nick = nick
            self.nick_list = nick_list
            self.botnick = botnick
            self.ops = ops
            self.logger = logger
            self.action = action
            self.privmsg = privmsg
            self.notice = notice
            self.msg_type = msg_type
            if cmd:
                self.cmd = cmd
                self.arg = arg
            if text != None:
                self.text = text
            if not (cmd==None or text==None):
                raise TypeError('missing cmd or text parameter')

    def start_logging(self):
        self.logger = log.logger
        if self.log_file:
            log.set_log_file(self.log_file)
        else:
            log.set_log_file('{}.log'.format(self.bot_nick))
        if self.log_level == 'error':
            level = logging.ERROR
        elif self.log_level == 'warning':
            level = logging.WARNING
        elif self.log_level == 'info':
            level = logging.INFO
        elif self.log_level == 'debug':
            level = logging.DEBUG
        # Set levels
        if self.log_level != "off":
            self.logger.setLevel(level)
        self.logger.info('Logging started!')

    def on_welcome(self, c, e):
        if self.ns_pass:
            self.logger.info('identifying with nickserv')
            c.privmsg(self.nickserv, 'identify {}'.format(self.ns_pass))
        for channel in self.chanlist:
            self.logger.info('joining channel {}'.format(channel.split()[0]))
            c.join(*channel.split())

    def on_pubmsg(self, c, e):
        self.process_event(c, e)

    def on_privmsg(self, c, e):
        self.process_event(c, e)

    def on_action(self, c, e):
        self.process_event(c, e)

    def call_help(self, nick, op):
        cmds = {k:v.help_text for k,v in plugin.cmds.items() if not plugin.cmds[k].ops}
        cmds.update({self.cmd_prefix + 'help': 'returns this output to private message'})
        if op:
            cmds.update({k:v.help_text for k,v in plugin.cmds.items() if plugin.cmds[k].ops})
            cmds.update({k:v for k,v in self.internal_commands.items()})
        helpout = OrderedDict(sorted(cmds.items()))
        for h in helpout:
            self.connection.privmsg(nick, '{} -- {}'.format(h, helpout[h]))
            time.sleep(.1)
        self.connection.privmsg(nick, 'List of listeners: {}'.format(', '.join([l for l in plugin.lstnrs])))
        return None

    def call_internal_commands(self, channel, nick, cmd, text, arg, c):
        if not cmd.startswith(self.cmd_prefix):
            return None
        else:
            cmd = cmd[len(self.cmd_prefix):]
        output = None
        if nick in self.ops:
            op = True
        else:
            op = False
        if cmd == 'join' and op:
            try:
                c.join(*arg.split())
                self.logger.info('joining {} per request of {}'.format(arg, nick))
                output = plugin.message('{}: joined {}'.format(nick, arg.split()[0]))
            except:
                self.logger.exception('issue with join command: {}join #channel <channel key>'.format(self.cmd_prefix))
        elif cmd == 'quit' and op:
            self.logger.info('quitting per request of {}'.format(nick))
            if not arg:
                arg = "See y'all later!"
            c.quit(arg)
            quit()
        elif cmd == 'help' and not self.disable_help:
            self.call_help(nick, op)
        elif cmd == 'reload' and op:
            self.logger.info('reloading plugins per request of {}'.format(nick))
            plugin.load_plugins(self.plugin_dir, use_prefix=self.use_prefix_for_plugins, cmd_prefix=self.cmd_prefix)
            output = plugin.message('Plugins reloaded')
        elif cmd == 'enable' and op:
            if arg in plugin.plugins:
                if plugin.plugins[arg].enabled:
                    output = plugin.message("{}: '{}' already enabled".format(nick, arg))
                else:
                    plugin.plugins[arg].enable()
                    output = plugin.message("{}: '{}' enabled!".format(nick, arg))
            else:
                output = plugin.message("{}: '{}' not found".format(nick, arg))
        elif cmd == 'disable' and op:
            if arg in plugin.plugins:
                if not plugin.plugins[arg].enabled:
                    output = plugin.message("{}: '{}' already disabled".format(nick, arg))
                else:
                    plugin.plugins[arg].disable()
                    output = plugin.message("{}: '{}' disabled!".format(nick, arg))
        elif cmd == 'op' and op:
            for o in arg.split(' '):
                self.ops.append(o)
            output = plugin.message('{}: {} added as op'.format(nick, arg))
        elif cmd == 'deop' and op:
            for o in arg.split(' '):
                self.ops = [i for i in self.ops if i != o]
            output = plugin.message('{}: {} removed as op'.format(nick, arg))
        elif cmd == 'ops' and op:
            output = plugin.message('current ops: {}'.format(', '.join(self.ops)))
        elif cmd == 'ban' and op:
            for o in arg.split(' '):
                self.banned_users.append(o)
            output = plugin.message('{}: banned {}'.format(nick, arg))
        elif cmd == 'unban' and op:
            for o in arg.split(' '):
                self.banned_users = [i for i in self.banned_users if i != o]
            output = plugin.message('{}: removed ban for {}'.format(nick, arg))
        elif cmd == 'banlist':
            output = plugin.message('currently banned: {}'.format(', '.join(self.banned_users)))
        return output

    def call_plugins(self, privmsg, action, notice, chan, cmd, text, nick_list, nick, arg, msg_type):
        output = None
        if cmd in plugin.cmds:
            try:
                if plugin.cmds[cmd].ops and nick not in self.ops:
                    if plugin.cmds[cmd].ops_msg:
                        output =  plugin.message(plugin.cmds[cmd].ops_msg)
                elif plugin.cmds[cmd].enabled:
                    self.logger.debug('executing {}'.format(cmd))
                    output = plugin.cmds[cmd].run(self.Message(
                        bot=self,
                        channel=chan,
                        cmd=cmd,
                        nick_list=nick_list,
                        nick=nick,
                        arg=arg,
                        privmsg=privmsg,
                        action=action,
                        notice=notice,
                        botnick=self.bot_nick,
                        ops=self.ops,
                        logger=self.logger,
                        msg_type=msg_type
                    ))
            except Exception:
                self.logger.exception('issue with command {}'.format(cmd))
        else:
            for lstnr in plugin.lstnrs:
                if plugin.lstnrs[lstnr].enabled:
                    try:
                        self.logger.debug('whispering to listener: {}'.format(lstnr))
                        listen_output = plugin.lstnrs[lstnr].run(self.Message(
                            bot=self,
                            channel=chan,
                            text=text,
                            nick_list=nick_list,
                            nick=nick,
                            privmsg=privmsg,
                            action=action,
                            notice=notice,
                            botnick=self.bot_nick,
                            ops=self.ops,
                            logger=self.logger,
                            msg_type=msg_type
                        ))
                        if listen_output:
                            output = listen_output
                    except Exception:
                        self.logger.exception('issue with listener {}'.format(lstnr))
        if output:
            self.logger.debug(f'returning output: {output.msg}')
        return output

    def process_event(self, c, e):
        nick = e.source.nick
        if nick == self.bot_nick:
            pass
        if e.arguments:
            text = e.arguments[0]
        else:
            text = ''
        if e.type == 'privmsg' or e.type == 'pubmsg':
            msg_type = 'message'
        else:
            msg_type = e.type
        if e.target == self.bot_nick:
            chan = nick
            nick_list = [nick]
        else:
            chan = e.target
            nick_list = list(self.channels[chan].users())
        if e.type == 'action':
            cmd = ''
        else:
            cmd = text.split(' ')[0]
        self.logger.debug(
            'Message info: channel: {}, nick: {}, cmd: {}, text: {}'.format(chan, nick, cmd, text)
        )
        if len(text.split(' ')) > 1:
            arg = ''.join([i + ' ' for i in text.split(' ')[1:]]).strip()
        else:
            arg = ''
        output = self.call_internal_commands(chan, nick, cmd, text, arg, c)
        if not output:
            plugin_info = {
                'chan': chan,
                'cmd': cmd,
                'text': text,
                'nick_list': nick_list,
                'nick': nick,
                'arg': arg,
                'privmsg': c.privmsg,
                'action': c.action,
                'notice': c.notice,
                'msg_type': msg_type
            }
            output = self.call_plugins(**plugin_info)
        if output:
            self.logger.debug(f'sending output: {output.msg}')
            self.process_output(c, chan, output)

    def process_output(self, c, chan, output):
        if not output.msg:
            return
        for msg in output.msg:
            if output.msg_type == plugin.OutputType.Message:
                self.logger.debug('output message: {}'.format(msg))
                try:
                    c.privmsg(chan, msg)
                except irc.client.MessageTooLong:
                    self.logger.error('output message too long: {}'.format(msg))
                    break
            elif output.msg_type == plugin.OutputType.Action:
                self.logger.debug('output action: {}'.format(msg))
                try:
                    c.action(chan, msg)
                except irc.client.MessageTooLong:
                    self.logger.error('output message too long: {}'.format(msg))
                    break
            else:
                self.logger.warning("Unsupported output type '{}'".format(output.msg_type))
            time.sleep(.5)

class TwitchBot(Bot):
    def __init__(self, nickname, channel, token, **kwargs):
        self.port = kwargs.get('port', 6667)
        self.ops = kwargs.get('ops', [])
        self.plugin_dir = kwargs.get('plugin_dir', 'plugins')
        self.log_level = kwargs.get('log_level', 'info')
        self.log_file = kwargs.get('log_file', None)
        self.server_pass = kwargs.get('server_pass', None)
        self.cmd_prefix = kwargs.get('cmd_prefix', '!')
        self.use_prefix_for_plugins = kwargs.get('use_prefix_for_plugins', False)
        self.disable_help = kwargs.get('disable_help', False)
        self.banned_users = kwargs.get('banned_users', [])
        self.bot_nick = nickname
        self.start_logging()
        self.channel = channel
        self.logger.info('Joining Twitch Server')
        irc.bot.SingleServerIRCBot.__init__(self, [('irc.twitch.tv', 6667, 'oauth:'+token)], nickname, nickname)
        self.internal_commands = {self.cmd_prefix + k: v for k,v in self.internal_commands.items()}
        plugin.load_plugins(self.plugin_dir, use_prefix=self.use_prefix_for_plugins, cmd_prefix=self.cmd_prefix)

    def on_welcome(self, c, e):
        self.logger.info('requesting permissions')
        c.cap('REQ', ':twitch.tv/membership')
        c.cap('REQ', ':twitch.tv/tags')
        c.cap('REQ', ':twitch.tv/commands')
        self.logger.info('Joining channel ' + self.channel)
        c.join(self.channel)
<|endoftext|>


<commit_before>from datetime import datetime, timezone
import imp
import logging
import os
import ssl
import time
import pinhook.plugin

import irc.bot

irc.client.ServerConnection.buffer_class.errors = 'replace'

class Bot(irc.bot.SingleServerIRCBot):
    def __init__(self, channels, nickname, server, **kwargs):
        self.port = kwargs.get('port', 6667)
        self.ops = kwargs.get('ops', [])
        self.plugin_dir = kwargs.get('plugin_dir', 'plugins')
        self.ssl_required = kwargs.get('ssl_required', False)
        self.ns_pass = kwargs.get('ns_pass', None)
        self.nickserv = kwargs.get('nickserv', 'NickServ')
        self.log_level = kwargs.get('log_level', 'info')
        self.server_pass = kwargs.get('server_pass', None)
        self.cmd_prefix = kwargs.get('cmd_prefix', '!')
        self.use_prefix_for_plugins = kwargs.get('use_prefix_for_plugins', False)
        if self.ssl_required:
            factory = irc.connection.Factory(wrapper=ssl.wrap_socket)
            irc.bot.SingleServerIRCBot.__init__(self, [(server, self.port, self.server_pass)], nickname, nickname, connect_factory=factory)
        else:
            irc.bot.SingleServerIRCBot.__init__(self, [(server, self.port, self.server_pass)], nickname, nickname)
        self.chanlist = channels
        self.bot_nick = nickname
        self.start_logging(self.log_level)
        self.output_message = pinhook.plugin.message
        self.output_action = pinhook.plugin.action
        self.load_plugins()

    class Message:
        def __init__(self, channel, nick, botnick, ops, logger, action, privmsg, notice, cmd=None, arg=None, text=None, nick_list=None):
            self.datetime = datetime.now(timezone.utc)
            self.timestamp = self.datetime.timestamp()
            self.channel = channel
            self.nick = nick
            self.nick_list = nick_list
            self.botnick = botnick
            self.ops = ops
            self.logger = logger
            self.action = action
            self.privmsg = privmsg
            self.notice = notice
            if cmd:
                self.cmd = cmd
                self.arg = arg
            if text:
                self.text = text
            if not (cmd or text):
                raise TypeError('missing cmd or text parameter')

    def start_logging(self, level):
        if level == 'error':
            level = logging.ERROR
        elif level == 'warning':
            level = logging.WARNING
        elif level == 'info':
            level = logging.INFO
        elif level == 'debug':
            level = logging.DEBUG
        self.logger = logging.getLogger(self.bot_nick)
        formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(module)s - %(message)s')
        # Set console logger
        ch = logging.StreamHandler()
        ch.setFormatter(formatter)
        # Set file logger
        fh = logging.FileHandler('{}.log'.format(self.bot_nick))
        fh.setFormatter(formatter)
        # Set levels
        if level != "off":
            self.logger.setLevel(level)
            ch.setLevel(level)
            fh.setLevel(level)
        # Add handlers
        self.logger.addHandler(ch)
        self.logger.addHandler(fh)
        self.logger.info('Logging started!')

    def load_plugins(self):
        # clear plugin list to ensure no old plugins remain
        self.logger.info('clearing plugin cache')
        pinhook.plugin.clear_plugins()
        # ensure plugin folder exists
        self.logger.info('checking plugin directory')
        if not os.path.exists(self.plugin_dir):
            self.logger.info('plugin directory {} not found, creating'.format(self.plugin_dir))
            os.makedirs(self.plugin_dir)
        # load all plugins
        for m in os.listdir(self.plugin_dir):
            if m.endswith('.py'):
                try:
                    name = m[:-3]
                    self.logger.info('loading plugin {}'.format(name))
                    fp, pathname, description = imp.find_module(name, [self.plugin_dir])
                    imp.load_module(name, fp, pathname, description)
                except Exception as e:
                    self.logger.exception('could not load plugin')
        # gather all commands and listeners
        if self.use_prefix_for_plugins: # use prefixes if needed
            pinhook.plugin.cmds = {self.cmd_prefix + k: v for k,v in pinhook.plugin.cmds.items()}
        for cmd in pinhook.plugin.cmds:
            self.logger.debug('adding command {}'.format(cmd))
        for lstnr in pinhook.plugin.lstnrs:
            self.logger.debug('adding listener {}'.format(lstnr))

    def on_welcome(self, c, e):
        if self.ns_pass:
            self.logger.info('identifying with nickserv')
            c.privmsg(self.nickserv, 'identify {}'.format(self.ns_pass))
        for channel in self.chanlist:
            self.logger.info('joining channel {}'.format(channel.split()[0]))
            c.join(*channel.split())

    def on_pubmsg(self, c, e):
        self.process_event(c, e)

    def on_privmsg(self, c, e):
        self.process_event(c, e)

    def on_action(self, c, e):
        self.process_event(c, e)

    def call_help(self, op):
        helplist = sorted([i for i in pinhook.plugin.cmds if op or not ('ops' in pinhook.plugin.cmds[i] and pinhook.plugin.cmds[i]['ops'])])
        msg = ', '.join(helplist)
        return self.output_message('Available commands: {}'.format(msg))

    def call_internal_commands(self, channel, nick, cmd, text, arg, c):
        output = None
        if nick in self.ops:
            op = True
        else:
            op = False
        if cmd == self.cmd_prefix + 'join' and op:
            c.join(*arg.split())
            self.logger.info('joining {} per request of {}'.format(arg, nick))
            output = self.output_message('{}: joined {}'.format(nick, arg.split()[0]))
        elif cmd == self.cmd_prefix + 'quit' and op:
            self.logger.info('quitting per request of {}'.format(nick))
            c.quit("See y'all later!")
            quit()
        elif cmd == self.cmd_prefix + 'help':
            output = self.call_help(op)
        elif cmd == self.cmd_prefix + 'reload' and op:
            self.logger.info('reloading plugins per request of {}'.format(nick))
            self.load_plugins()
            output = self.output_message('Plugins reloaded')
        return output

    def call_plugins(self, privmsg, action, notice, chan, cmd, text, nick_list, nick, arg):
        output = None
        if cmd in pinhook.plugin.cmds:
            try:
                if 'ops' in pinhook.plugin.cmds[cmd] and nick not in self.ops:
                    if pinhook.plugin.cmds[cmd]['ops_msg']:
                        output =  self.output_message(pinhook.plugin.cmds[cmd]['ops_msg'])
                else:
                    self.logger.debug('executing {}'.format(cmd))
                    output = pinhook.plugin.cmds[cmd]['run'](self.Message(
                        channel=chan,
                        cmd=cmd,
                        nick_list=nick_list,
                        nick=nick,
                        arg=arg,
                        privmsg=privmsg,
                        action=action,
                        notice=notice,
                        botnick=self.bot_nick,
                        ops=self.ops,
                        logger=self.logger
                    ))
                    self.logger.debug(output)
            except Exception as e:
                self.logger.exception('issue with command {}'.format(cmd))
        else:
            for lstnr in pinhook.plugin.lstnrs:
                try:
                    self.logger.debug('whispering to listener: {}'.format(lstnr))
                    output = pinhook.plugin.lstnrs[lstnr](self.Message(
                        channel=chan,
                        text=text,
                        nick_list=nick_list,
                        nick=nick,
                        privmsg=privmsg,
                        action=action,
                        notice=notice,
                        botnick=self.bot_nick,
                        ops=self.ops,
                        logger=self.logger
                    ))
                    self.logger.debug(output.event_type)
                    self.logger.debug(output.msg)
                except Exception as e:
                    self.logger.exception('issue with listener {}'.format(lstnr))
        return output

    def process_event(self, c, e):
        nick = e.source.nick
        text = e.arguments[0]
        if e.target == self.bot_nick:
            chan = nick
            nick_list = [nick]
        else:
            chan = e.target
            nick_list = list(self.channels[chan].users())
        if e.type == 'action':
            cmd = ''
        else:
            cmd = text.split(' ')[0]
        self.logger.debug(
            'Message info: channel: {}, nick: {}, cmd: {}, text: {}'.format(chan, nick, cmd, text)
        )
        if len(text.split(' ')) > 1:
            arg = ''.join([i + ' ' for i in text.split(' ')[1:]]).strip()
        else:
            arg = ''
        output = self.call_internal_commands(chan, nick, cmd, text, arg, c)
        if not output:
            plugin_info = {
                'chan': chan,
                'cmd': cmd,
                'text': text,
                'nick_list': nick_list,
                'nick': nick,
                'arg': arg,
                'privmsg': c.privmsg,
                'action': c.action,
                'notice': c.notice,
            }
            output = self.call_plugins(**plugin_info)
        if output:
            self.process_output(c, chan, output)

    def process_output(self, c, chan, output):
        if not output.msg:
            return
        for msg in output.msg:
            if len(msg.encode('UTF-8')) > 512:
                self.logger.error('output message too long: {}'.format(msg))
            elif output.msg_type == pinhook.plugin.OutputType.Message:
                self.logger.debug('output message: {}'.format(msg))
                c.privmsg(chan, msg)
            elif output.msg_type == pinhook.plugin.OutputType.Action:
                self.logger.debug('output action: {}'.format(msg))
                c.action(chan, msg)
            else:
                self.logger.warning("Unsupported output type '{}'".format(output.msg_type))
            time.sleep(.5)

class TwitchBot(Bot):
    def __init__(self, nickname, channel, token, plugin_dir='plugins', log_level='info', ops=[]):
        self.bot_nick = nickname
        self.start_logging(log_level)
        self.channel = channel
        self.plugin_dir = plugin_dir
        self.ops = ops
        server = 'irc.twitch.tv'
        port = 6667
        self.logger.info('Joining Twitch Server')
        irc.bot.SingleServerIRCBot.__init__(self, [(server, port, 'oauth:'+token)], nickname, nickname)
        self.load_plugins()

    def on_welcome(self, c, e):
        self.logger.info('requesting permissions')
        c.cap('REQ', ':twitch.tv/membership')
        c.cap('REQ', ':twitch.tv/tags')
        c.cap('REQ', ':twitch.tv/commands')
        self.logger.info('Joining channel ' + self.channel)
        c.join(self.channel)

<commit_msg>fix typo<commit_after>from datetime import datetime, timezone
import imp
import logging
import os
import ssl
import time
import pinhook.plugin

import irc.bot

irc.client.ServerConnection.buffer_class.errors = 'replace'

class Bot(irc.bot.SingleServerIRCBot):
    def __init__(self, channels, nickname, server, **kwargs):
        self.port = kwargs.get('port', 6667)
        self.ops = kwargs.get('ops', [])
        self.plugin_dir = kwargs.get('plugin_dir', 'plugins')
        self.ssl_required = kwargs.get('ssl_required', False)
        self.ns_pass = kwargs.get('ns_pass', None)
        self.nickserv = kwargs.get('nickserv', 'NickServ')
        self.log_level = kwargs.get('log_level', 'info')
        self.server_pass = kwargs.get('server_pass', None)
        self.cmd_prefix = kwargs.get('cmd_prefix', '!')
        self.use_prefix_for_plugins = kwargs.get('use_prefix_for_plugins', False)
        if self.ssl_required:
            factory = irc.connection.Factory(wrapper=ssl.wrap_socket)
            irc.bot.SingleServerIRCBot.__init__(self, [(server, self.port, self.server_pass)], nickname, nickname, connect_factory=factory)
        else:
            irc.bot.SingleServerIRCBot.__init__(self, [(server, self.port, self.server_pass)], nickname, nickname)
        self.chanlist = channels
        self.bot_nick = nickname
        self.start_logging(self.log_level)
        self.output_message = pinhook.plugin.message
        self.output_action = pinhook.plugin.action
        self.load_plugins()

    class Message:
        def __init__(self, channel, nick, botnick, ops, logger, action, privmsg, notice, cmd=None, arg=None, text=None, nick_list=None):
            self.datetime = datetime.now(timezone.utc)
            self.timestamp = self.datetime.timestamp()
            self.channel = channel
            self.nick = nick
            self.nick_list = nick_list
            self.botnick = botnick
            self.ops = ops
            self.logger = logger
            self.action = action
            self.privmsg = privmsg
            self.notice = notice
            if cmd:
                self.cmd = cmd
                self.arg = arg
            if text:
                self.text = text
            if not (cmd or text):
                raise TypeError('missing cmd or text parameter')

    def start_logging(self, level):
        if level == 'error':
            level = logging.ERROR
        elif level == 'warning':
            level = logging.WARNING
        elif level == 'info':
            level = logging.INFO
        elif level == 'debug':
            level = logging.DEBUG
        self.logger = logging.getLogger(self.bot_nick)
        formatter = logging.Formatter('%(asctime)s - %(levelname)s - %(module)s - %(message)s')
        # Set console logger
        ch = logging.StreamHandler()
        ch.setFormatter(formatter)
        # Set file logger
        fh = logging.FileHandler('{}.log'.format(self.bot_nick))
        fh.setFormatter(formatter)
        # Set levels
        if level != "off":
            self.logger.setLevel(level)
            ch.setLevel(level)
            fh.setLevel(level)
        # Add handlers
        self.logger.addHandler(ch)
        self.logger.addHandler(fh)
        self.logger.info('Logging started!')

    def load_plugins(self):
        # clear plugin list to ensure no old plugins remain
        self.logger.info('clearing plugin cache')
        pinhook.plugin.clear_plugins()
        # ensure plugin folder exists
        self.logger.info('checking plugin directory')
        if not os.path.exists(self.plugin_dir):
            self.logger.info('plugin directory {} not found, creating'.format(self.plugin_dir))
            os.makedirs(self.plugin_dir)
        # load all plugins
        for m in os.listdir(self.plugin_dir):
            if m.endswith('.py'):
                try:
                    name = m[:-3]
                    self.logger.info('loading plugin {}'.format(name))
                    fp, pathname, description = imp.find_module(name, [self.plugin_dir])
                    imp.load_module(name, fp, pathname, description)
                except Exception as e:
                    self.logger.exception('could not load plugin')
        # gather all commands and listeners
        if self.use_prefix_for_plugins: # use prefixes if needed
            pinhook.plugin.cmds = {self.cmd_prefix + k: v for k,v in pinhook.plugin.cmds.items()}
        for cmd in pinhook.plugin.cmds:
            self.logger.debug('adding command {}'.format(cmd))
        for lstnr in pinhook.plugin.lstnrs:
            self.logger.debug('adding listener {}'.format(lstnr))

    def on_welcome(self, c, e):
        if self.ns_pass:
            self.logger.info('identifying with nickserv')
            c.privmsg(self.nickserv, 'identify {}'.format(self.ns_pass))
        for channel in self.chanlist:
            self.logger.info('joining channel {}'.format(channel.split()[0]))
            c.join(*channel.split())

    def on_pubmsg(self, c, e):
        self.process_event(c, e)

    def on_privmsg(self, c, e):
        self.process_event(c, e)

    def on_action(self, c, e):
        self.process_event(c, e)

    def call_help(self, op):
        helplist = sorted([i for i in pinhook.plugin.cmds if op or not ('ops' in pinhook.plugin.cmds[i] and pinhook.plugin.cmds[i]['ops'])])
        msg = ', '.join(helplist)
        return self.output_message('Available commands: {}'.format(msg))

    def call_internal_commands(self, channel, nick, cmd, text, arg, c):
        output = None
        if nick in self.ops:
            op = True
        else:
            op = False
        if cmd == self.cmd_prefix + 'join' and op:
            c.join(*arg.split())
            self.logger.info('joining {} per request of {}'.format(arg, nick))
            output = self.output_message('{}: joined {}'.format(nick, arg.split()[0]))
        elif cmd == self.cmd_prefix + 'quit' and op:
            self.logger.info('quitting per request of {}'.format(nick))
            c.quit("See y'all later!")
            quit()
        elif cmd == self.cmd_prefix + 'help':
            output = self.call_help(op)
        elif cmd == self.cmd_prefix + 'reload' and op:
            self.logger.info('reloading plugins per request of {}'.format(nick))
            self.load_plugins()
            output = self.output_message('Plugins reloaded')
        return output

    def call_plugins(self, privmsg, action, notice, chan, cmd, text, nick_list, nick, arg):
        output = None
        if cmd in pinhook.plugin.cmds:
            try:
                if 'ops' in pinhook.plugin.cmds[cmd] and nick not in self.ops:
                    if pinhook.plugin.cmds[cmd]['ops_msg']:
                        output =  self.output_message(pinhook.plugin.cmds[cmd]['ops_msg'])
                else:
                    self.logger.debug('executing {}'.format(cmd))
                    output = pinhook.plugin.cmds[cmd]['run'](self.Message(
                        channel=chan,
                        cmd=cmd,
                        nick_list=nick_list,
                        nick=nick,
                        arg=arg,
                        privmsg=privmsg,
                        action=action,
                        notice=notice,
                        botnick=self.bot_nick,
                        ops=self.ops,
                        logger=self.logger
                    ))
                    self.logger.debug(output)
            except Exception as e:
                self.logger.exception('issue with command {}'.format(cmd))
        else:
            for lstnr in pinhook.plugin.lstnrs:
                try:
                    self.logger.debug('whispering to listener: {}'.format(lstnr))
                    output = pinhook.plugin.lstnrs[lstnr](self.Message(
                        channel=chan,
                        text=text,
                        nick_list=nick_list,
                        nick=nick,
                        privmsg=privmsg,
                        action=action,
                        notice=notice,
                        botnick=self.bot_nick,
                        ops=self.ops,
                        logger=self.logger
                    ))
                    self.logger.debug(output.msg_type)
                    self.logger.debug(output.msg)
                except Exception as e:
                    self.logger.exception('issue with listener {}'.format(lstnr))
        return output

    def process_event(self, c, e):
        nick = e.source.nick
        text = e.arguments[0]
        if e.target == self.bot_nick:
            chan = nick
            nick_list = [nick]
        else:
            chan = e.target
            nick_list = list(self.channels[chan].users())
        if e.type == 'action':
            cmd = ''
        else:
            cmd = text.split(' ')[0]
        self.logger.debug(
            'Message info: channel: {}, nick: {}, cmd: {}, text: {}'.format(chan, nick, cmd, text)
        )
        if len(text.split(' ')) > 1:
            arg = ''.join([i + ' ' for i in text.split(' ')[1:]]).strip()
        else:
            arg = ''
        output = self.call_internal_commands(chan, nick, cmd, text, arg, c)
        if not output:
            plugin_info = {
                'chan': chan,
                'cmd': cmd,
                'text': text,
                'nick_list': nick_list,
                'nick': nick,
                'arg': arg,
                'privmsg': c.privmsg,
                'action': c.action,
                'notice': c.notice,
            }
            output = self.call_plugins(**plugin_info)
        if output:
            self.process_output(c, chan, output)

    def process_output(self, c, chan, output):
        if not output.msg:
            return
        for msg in output.msg:
            if len(msg.encode('UTF-8')) > 512:
                self.logger.error('output message too long: {}'.format(msg))
            elif output.msg_type == pinhook.plugin.OutputType.Message:
                self.logger.debug('output message: {}'.format(msg))
                c.privmsg(chan, msg)
            elif output.msg_type == pinhook.plugin.OutputType.Action:
                self.logger.debug('output action: {}'.format(msg))
                c.action(chan, msg)
            else:
                self.logger.warning("Unsupported output type '{}'".format(output.msg_type))
            time.sleep(.5)

class TwitchBot(Bot):
    def __init__(self, nickname, channel, token, plugin_dir='plugins', log_level='info', ops=[]):
        self.bot_nick = nickname
        self.start_logging(log_level)
        self.channel = channel
        self.plugin_dir = plugin_dir
        self.ops = ops
        server = 'irc.twitch.tv'
        port = 6667
        self.logger.info('Joining Twitch Server')
        irc.bot.SingleServerIRCBot.__init__(self, [(server, port, 'oauth:'+token)], nickname, nickname)
        self.load_plugins()

    def on_welcome(self, c, e):
        self.logger.info('requesting permissions')
        c.cap('REQ', ':twitch.tv/membership')
        c.cap('REQ', ':twitch.tv/tags')
        c.cap('REQ', ':twitch.tv/commands')
        self.logger.info('Joining channel ' + self.channel)
        c.join(self.channel)

<|endoftext|>


<commit_before># -*- coding: utf-8 -*-

import os
import bson
import logging

import furl
import pymongo

from modularodm import fields, Q
from dateutil.parser import parse as parse_date
from modularodm.exceptions import NoResultsFound

from framework.auth import Auth
from framework.mongo import StoredObject
from framework.analytics import get_basic_counters

from website.models import NodeLog
from website.addons.base import AddonNodeSettingsBase, GuidFile

from website.addons.osfstorage import logs
from website.addons.osfstorage import utils
from website.addons.osfstorage import errors
from website.addons.osfstorage import settings

logger = logging.getLogger(__name__)

class OsfStorageNodeSettings(AddonNodeSettingsBase):
    complete = True
    has_auth = True
    root_node = fields.ForeignField('OsfStorageFileNode')
    file_tree = fields.ForeignField('OsfStorageFileTree')

    def on_add(self):
        if self.root_node:
            return

        self.save()
        root = OsfStorageFileNode(name='', kind='folder', node_settings=self)
        root.save()
        self.root_node = root
        self.save()

    def find_or_create_file_guid(self, path):
        return OsfStorageGuidFile.get_or_create(self.owner, path)

    def after_fork(self, node, fork, user, save=True):
        clone, message = super(OsfStorageNodeSettings, self).after_fork(
            node=node, fork=fork, user=user, save=False
        )
        clone.save()
        clone.root_node = utils.copy_files(self.root_node, clone)
        clone.save()

        return clone, message

    def after_register(self, node, registration, user, save=True):
        clone = self.clone()
        clone.owner = registration
        clone.save()

        clone.root_node = utils.copy_files(self.root_node, clone)
        clone.save()

        return clone, None

    def serialize_waterbutler_settings(self):
        ret = {
            'callback': self.owner.api_url_for(
                'osf_storage_update_metadata_hook',
                _absolute=True,
                _offload=True
            ),
            'metadata': self.owner.api_url_for(
                'osf_storage_get_metadata_hook',
                _absolute=True,
                _offload=True
            ),
            'revisions': self.owner.api_url_for(
                'osf_storage_get_revisions',
                _absolute=True,
                _offload=True
            ),
            'createFolder': self.owner.api_url_for(
                'osf_storage_create_folder',
                _absolute=True,
            ),
        }
        ret.update(settings.WATERBUTLER_SETTINGS)
        return ret

    def serialize_waterbutler_credentials(self):
        return settings.WATERBUTLER_CREDENTIALS

    def create_waterbutler_log(self, auth, action, metadata):
        pass

class OsfStorageFileNode(StoredObject):
    __indices__ = [
        {
            'key_or_list': [
                ('name', pymongo.ASCENDING),
                ('kind', pymongo.ASCENDING),
                ('parent', pymongo.ASCENDING),
                ('node_settings', pymongo.ASCENDING),
            ],
            'unique': True,
        }
    ]

    _id = fields.StringField(primary=True, default=lambda: str(bson.ObjectId()))

    is_deleted = fields.BooleanField(default=False)
    name = fields.StringField(required=True, index=True)
    kind = fields.StringField(required=True, index=True)
    parent = fields.ForeignField('OsfStorageFileNode', index=True)
    versions = fields.ForeignField('OsfStorageFileVersion', list=True)
    node_settings = fields.ForeignField('OsfStorageNodeSettings', required=True, index=True)

    @classmethod
    def get(cls, path, node_settings):
        return cls.find_one(
            Q('_id', 'eq', path) &
            Q('node_settings', 'eq', node_settings)
        )

    @classmethod
    def get_folder(cls, path, node_settings):
        return cls.find_one(
            Q('_id', 'eq', path) &
            Q('kind', 'eq', 'folder') &
            Q('node_settings', 'eq', node_settings)
        )

    @classmethod
    def get_file(cls, path, node_settings):
        return cls.find_one(
            Q('_id', 'eq', path) &
            Q('kind', 'eq', 'file') &
            Q('node_settings', 'eq', node_settings)
        )

    @property
    @utils.must_be('folder')
    def children(self):
        return self.__class__.find(Q('parent', 'eq', self._id))

    @property
    def is_folder(self):
        return self.kind == 'folder'

    @property
    def is_file(self):
        return self.kind == 'file'

    @property
    def path(self):
        return '/{}{}'.format(self._id, '/' if self.is_folder else '')

    @property
    def node(self):
        return self.node_settings.owner

    @utils.must_be('folder')
    def find_child_by_name(self, name, kind='file'):
        return self.__class__.find_one(
            Q('name', 'eq', name) &
            Q('kind', 'eq', kind) &
            Q('parent', 'eq', self)
        )

    def append_folder(self, name, save=True):
        return self._create_child(name, 'folder', save=save)

    def append_file(self, name, save=True):
        return self._create_child(name, 'file', save=save)

    @utils.must_be('folder')
    def _create_child(self, name, kind, save=True):
        child = OsfStorageFileNode(
            name=name,
            kind=kind,
            parent=self,
            node_settings=self.node_settings
        )
        if save:
            child.save()
        return child

    def get_download_count(self, version=None):
        if self.is_folder:
            return None

        parts = ['download', self.node._id, self.path]
        if version is not None:
            parts.append(version)
        page = ':'.join([format(part) for part in parts])
        _, count = get_basic_counters(page)

        return count or 0

    @utils.must_be('file')
    def get_version(self, index=-1, required=False):
        try:
            return self.versions[index]
        except IndexError:
            if required:
                raise errors.VersionNotFoundError
            return None

    @utils.must_be('file')
    def create_version(self, creator, location, metadata=None):
        latest_version = self.get_version()
        version = OsfStorageFileVersion(creator=creator, location=location)

        if latest_version and latest_version.is_duplicate(version):
            if self.is_deleted:
                self.undelete(Auth(creator))
            return latest_version

        if metadata:
            version.update_metadata(metadata)

        version.save()
        self.versions.append(version)
        self.is_deleted = False
        self.save()
        self.log(
            Auth(creator),
            NodeLog.FILE_UPDATED if len(self.versions) > 1 else NodeLog.FILE_ADDED,
        )
        return version

    @utils.must_be('file')
    def update_version_metadata(self, location, metadata):
        assert self.is_file, 'Must be a file to perform this action'

        for version in reversed(self.versions):
            if version.location == location:
                version.update_metadata(metadata)
                return
        raise errors.VersionNotFoundError

    def log(self, auth, action, version=True):
        node_logger = logs.OsfStorageNodeLogger(
            auth=auth,
            node=self.node,
            path=self.path,
        )
        extra = {'version': len(self.versions)} if version else None
        node_logger.log(action, extra=extra, save=True)

    def delete(self, auth, recurse=True, log=True):
        if self.is_deleted:
            raise errors.DeleteError

        self.is_deleted = True
        self.save()

        if recurse and self.is_folder:
            for child in self.children:
                child.delete(auth, recurse=recurse, log=log)

        if log:
            self.log(auth, NodeLog.FILE_REMOVED, version=False)

    def undelete(self, auth, recurse=True, log=True):
        if not self.is_deleted:
            raise errors.UndeleteError

        self.is_deleted = False
        self.save()

        if recurse and self.is_folder:
            for child in self.children:
                child.undelete(auth, recurse=recurse, log=log)

        if log:
            self.log(auth, NodeLog.FILE_ADDED)

    def __repr__(self):
        return '<{}(name={!r}, node_settings={!r})>'.format(
            self.__class__.__name__,
            self.name,
            self.to_storage()['node_settings']
        )

class OsfStorageFileVersion(StoredObject):

    _id = fields.StringField(primary=True, default=lambda: str(bson.ObjectId()))
    creator = fields.ForeignField('user', required=True)

    # Date version record was created. This is the date displayed to the user.
    date_created = fields.DateTimeField(auto_now_add=True)

    # Dictionary specifying all information needed to locate file on backend
    # {
    #     'service': 'cloudfiles',  # required
    #     'container': 'osf',       # required
    #     'object': '20c53b',       # required
    #     'worker_url': '127.0.0.1',
    #     'worker_host': 'upload-service-1',
    # }
    location = fields.DictionaryField(validate=utils.validate_location)

    # Dictionary containing raw metadata from upload service response
    # {
    #     'size': 1024,                            # required
    #     'content_type': 'text/plain',            # required
    #     'date_modified': '2014-11-07T20:24:15',  # required
    #     'md5': 'd077f2',
    # }
    metadata = fields.DictionaryField()

    size = fields.IntegerField()
    content_type = fields.StringField()
    # Date file modified on third-party backend. Not displayed to user, since
    # this date may be earlier than the date of upload if the file already
    # exists on the backend
    date_modified = fields.DateTimeField()

    @property
    def location_hash(self):
        return self.location['object']

    def is_duplicate(self, other):
        return self.location_hash == other.location_hash

    def update_metadata(self, metadata):
        self.metadata.update(metadata)
        self.content_type = self.metadata.get('contentType', None)
        try:
            self.size = self.metadata['size']
            self.date_modified = parse_date(self.metadata['modified'], ignoretz=True)
        except KeyError as err:
            raise errors.MissingFieldError(str(err))
        self.save()

class OsfStorageGuidFile(GuidFile):
    provider = 'osfstorage'
    version_identifier = 'version'
    __indices__ = [
        {
            'key_or_list': [
                ('node', pymongo.ASCENDING),
                ('path', pymongo.ASCENDING),
            ],
            'unique': True,
        }
    ]

    path = fields.StringField(required=True, index=True)

    @classmethod
    def get_or_create(cls, node, path):
        try:
            return cls.find_one(
                Q('node', 'eq', node) &
                Q('path', 'eq', path)
            ), False
        except NoResultsFound:
            # Create new
            new = cls(node=node, path=path)
            new.save()
        return new, True

    @property
    def waterbutler_path(self):
        return self.path

    @property
    def unique_identifier(self):
        return self._metadata_cache['extra']['version']

    @property
    def file_url(self):
        return os.path.join('osfstorage', 'files', self.path.lstrip('/'))

    def get_download_path(self, version_idx):
        url = furl.furl('/{0}/'.format(self._id))
        url.args.update({
            'action': 'download',
            'version': version_idx,
            'mode': 'render',
        })
        return url.url
<commit_msg>Comment out old root model<commit_after># -*- coding: utf-8 -*-

import os
import bson
import logging

import furl
import pymongo

from modularodm import fields, Q
from dateutil.parser import parse as parse_date
from modularodm.exceptions import NoResultsFound

from framework.auth import Auth
from framework.mongo import StoredObject
from framework.analytics import get_basic_counters

from website.models import NodeLog
from website.addons.base import AddonNodeSettingsBase, GuidFile

from website.addons.osfstorage import logs
from website.addons.osfstorage import utils
from website.addons.osfstorage import errors
from website.addons.osfstorage import settings

logger = logging.getLogger(__name__)

class OsfStorageNodeSettings(AddonNodeSettingsBase):
    complete = True
    has_auth = True
    root_node = fields.ForeignField('OsfStorageFileNode')
    # file_tree = fields.ForeignField('OsfStorageFileTree')

    def on_add(self):
        if self.root_node:
            return

        self.save()
        root = OsfStorageFileNode(name='', kind='folder', node_settings=self)
        root.save()
        self.root_node = root
        self.save()

    def find_or_create_file_guid(self, path):
        return OsfStorageGuidFile.get_or_create(self.owner, path)

    def after_fork(self, node, fork, user, save=True):
        clone, message = super(OsfStorageNodeSettings, self).after_fork(
            node=node, fork=fork, user=user, save=False
        )
        clone.save()
        clone.root_node = utils.copy_files(self.root_node, clone)
        clone.save()

        return clone, message

    def after_register(self, node, registration, user, save=True):
        clone = self.clone()
        clone.owner = registration
        clone.save()

        clone.root_node = utils.copy_files(self.root_node, clone)
        clone.save()

        return clone, None

    def serialize_waterbutler_settings(self):
        ret = {
            'callback': self.owner.api_url_for(
                'osf_storage_update_metadata_hook',
                _absolute=True,
                _offload=True
            ),
            'metadata': self.owner.api_url_for(
                'osf_storage_get_metadata_hook',
                _absolute=True,
                _offload=True
            ),
            'revisions': self.owner.api_url_for(
                'osf_storage_get_revisions',
                _absolute=True,
                _offload=True
            ),
            'createFolder': self.owner.api_url_for(
                'osf_storage_create_folder',
                _absolute=True,
            ),
        }
        ret.update(settings.WATERBUTLER_SETTINGS)
        return ret

    def serialize_waterbutler_credentials(self):
        return settings.WATERBUTLER_CREDENTIALS

    def create_waterbutler_log(self, auth, action, metadata):
        pass

class OsfStorageFileNode(StoredObject):
    __indices__ = [
        {
            'key_or_list': [
                ('name', pymongo.ASCENDING),
                ('kind', pymongo.ASCENDING),
                ('parent', pymongo.ASCENDING),
                ('node_settings', pymongo.ASCENDING),
            ],
            'unique': True,
        }
    ]

    _id = fields.StringField(primary=True, default=lambda: str(bson.ObjectId()))

    is_deleted = fields.BooleanField(default=False)
    name = fields.StringField(required=True, index=True)
    kind = fields.StringField(required=True, index=True)
    parent = fields.ForeignField('OsfStorageFileNode', index=True)
    versions = fields.ForeignField('OsfStorageFileVersion', list=True)
    node_settings = fields.ForeignField('OsfStorageNodeSettings', required=True, index=True)

    @classmethod
    def get(cls, path, node_settings):
        return cls.find_one(
            Q('_id', 'eq', path) &
            Q('node_settings', 'eq', node_settings)
        )

    @classmethod
    def get_folder(cls, path, node_settings):
        return cls.find_one(
            Q('_id', 'eq', path) &
            Q('kind', 'eq', 'folder') &
            Q('node_settings', 'eq', node_settings)
        )

    @classmethod
    def get_file(cls, path, node_settings):
        return cls.find_one(
            Q('_id', 'eq', path) &
            Q('kind', 'eq', 'file') &
            Q('node_settings', 'eq', node_settings)
        )

    @property
    @utils.must_be('folder')
    def children(self):
        return self.__class__.find(Q('parent', 'eq', self._id))

    @property
    def is_folder(self):
        return self.kind == 'folder'

    @property
    def is_file(self):
        return self.kind == 'file'

    @property
    def path(self):
        return '/{}{}'.format(self._id, '/' if self.is_folder else '')

    @property
    def node(self):
        return self.node_settings.owner

    @utils.must_be('folder')
    def find_child_by_name(self, name, kind='file'):
        return self.__class__.find_one(
            Q('name', 'eq', name) &
            Q('kind', 'eq', kind) &
            Q('parent', 'eq', self)
        )

    def append_folder(self, name, save=True):
        return self._create_child(name, 'folder', save=save)

    def append_file(self, name, save=True):
        return self._create_child(name, 'file', save=save)

    @utils.must_be('folder')
    def _create_child(self, name, kind, save=True):
        child = OsfStorageFileNode(
            name=name,
            kind=kind,
            parent=self,
            node_settings=self.node_settings
        )
        if save:
            child.save()
        return child

    def get_download_count(self, version=None):
        if self.is_folder:
            return None

        parts = ['download', self.node._id, self.path]
        if version is not None:
            parts.append(version)
        page = ':'.join([format(part) for part in parts])
        _, count = get_basic_counters(page)

        return count or 0

    @utils.must_be('file')
    def get_version(self, index=-1, required=False):
        try:
            return self.versions[index]
        except IndexError:
            if required:
                raise errors.VersionNotFoundError
            return None

    @utils.must_be('file')
    def create_version(self, creator, location, metadata=None):
        latest_version = self.get_version()
        version = OsfStorageFileVersion(creator=creator, location=location)

        if latest_version and latest_version.is_duplicate(version):
            if self.is_deleted:
                self.undelete(Auth(creator))
            return latest_version

        if metadata:
            version.update_metadata(metadata)

        version.save()
        self.versions.append(version)
        self.is_deleted = False
        self.save()
        self.log(
            Auth(creator),
            NodeLog.FILE_UPDATED if len(self.versions) > 1 else NodeLog.FILE_ADDED,
        )
        return version

    @utils.must_be('file')
    def update_version_metadata(self, location, metadata):
        assert self.is_file, 'Must be a file to perform this action'

        for version in reversed(self.versions):
            if version.location == location:
                version.update_metadata(metadata)
                return
        raise errors.VersionNotFoundError

    def log(self, auth, action, version=True):
        node_logger = logs.OsfStorageNodeLogger(
            auth=auth,
            node=self.node,
            path=self.path,
        )
        extra = {'version': len(self.versions)} if version else None
        node_logger.log(action, extra=extra, save=True)

    def delete(self, auth, recurse=True, log=True):
        if self.is_deleted:
            raise errors.DeleteError

        self.is_deleted = True
        self.save()

        if recurse and self.is_folder:
            for child in self.children:
                child.delete(auth, recurse=recurse, log=log)

        if log:
            self.log(auth, NodeLog.FILE_REMOVED, version=False)

    def undelete(self, auth, recurse=True, log=True):
        if not self.is_deleted:
            raise errors.UndeleteError

        self.is_deleted = False
        self.save()

        if recurse and self.is_folder:
            for child in self.children:
                child.undelete(auth, recurse=recurse, log=log)

        if log:
            self.log(auth, NodeLog.FILE_ADDED)

    def __repr__(self):
        return '<{}(name={!r}, node_settings={!r})>'.format(
            self.__class__.__name__,
            self.name,
            self.to_storage()['node_settings']
        )

class OsfStorageFileVersion(StoredObject):

    _id = fields.StringField(primary=True, default=lambda: str(bson.ObjectId()))
    creator = fields.ForeignField('user', required=True)

    # Date version record was created. This is the date displayed to the user.
    date_created = fields.DateTimeField(auto_now_add=True)

    # Dictionary specifying all information needed to locate file on backend
    # {
    #     'service': 'cloudfiles',  # required
    #     'container': 'osf',       # required
    #     'object': '20c53b',       # required
    #     'worker_url': '127.0.0.1',
    #     'worker_host': 'upload-service-1',
    # }
    location = fields.DictionaryField(validate=utils.validate_location)

    # Dictionary containing raw metadata from upload service response
    # {
    #     'size': 1024,                            # required
    #     'content_type': 'text/plain',            # required
    #     'date_modified': '2014-11-07T20:24:15',  # required
    #     'md5': 'd077f2',
    # }
    metadata = fields.DictionaryField()

    size = fields.IntegerField()
    content_type = fields.StringField()
    # Date file modified on third-party backend. Not displayed to user, since
    # this date may be earlier than the date of upload if the file already
    # exists on the backend
    date_modified = fields.DateTimeField()

    @property
    def location_hash(self):
        return self.location['object']

    def is_duplicate(self, other):
        return self.location_hash == other.location_hash

    def update_metadata(self, metadata):
        self.metadata.update(metadata)
        self.content_type = self.metadata.get('contentType', None)
        try:
            self.size = self.metadata['size']
            self.date_modified = parse_date(self.metadata['modified'], ignoretz=True)
        except KeyError as err:
            raise errors.MissingFieldError(str(err))
        self.save()

class OsfStorageGuidFile(GuidFile):
    provider = 'osfstorage'
    version_identifier = 'version'
    __indices__ = [
        {
            'key_or_list': [
                ('node', pymongo.ASCENDING),
                ('path', pymongo.ASCENDING),
            ],
            'unique': True,
        }
    ]

    path = fields.StringField(required=True, index=True)

    @classmethod
    def get_or_create(cls, node, path):
        try:
            return cls.find_one(
                Q('node', 'eq', node) &
                Q('path', 'eq', path)
            ), False
        except NoResultsFound:
            # Create new
            new = cls(node=node, path=path)
            new.save()
        return new, True

    @property
    def waterbutler_path(self):
        return self.path

    @property
    def unique_identifier(self):
        return self._metadata_cache['extra']['version']

    @property
    def file_url(self):
        return os.path.join('osfstorage', 'files', self.path.lstrip('/'))

    def get_download_path(self, version_idx):
        url = furl.furl('/{0}/'.format(self._id))
        url.args.update({
            'action': 'download',
            'version': version_idx,
            'mode': 'render',
        })
        return url.url
<|endoftext|>


<commit_before>import os
import logging

from capture_gui.vendor.Qt import QtCore, QtWidgets
from capture_gui import plugin, lib

log = logging.getLogger("IO")

class IoAction(QtWidgets.QAction):

    def __init__(self, parent, filepath):
        super(IoAction, self).__init__(parent)

        action_label = os.path.basename(filepath)

        self.setText(action_label)
        self.setData(filepath)

        # check if file exists and disable when false
        self.setEnabled(os.path.isfile(filepath))

        # get icon from file
        info = QtCore.QFileInfo(filepath)
        icon_provider = QtWidgets.QFileIconProvider()
        self.setIcon(icon_provider.icon(info))

        self.triggered.connect(self.open_object_data)

    def open_object_data(self):
        lib.open_file(self.data())

class IoPlugin(plugin.Plugin):
    """Codec widget.

    Allows to set format, compression and quality.

    """
    id = "IO"
    label = "Save"
    section = "app"
    order = 40

    def __init__(self, parent=None):
        super(IoPlugin, self).__init__(parent=parent)

        self.recent_playblasts = list()

        self._layout = QtWidgets.QVBoxLayout()
        self._layout.setContentsMargins(0, 0, 0, 0)
        self.setLayout(self._layout)

        # region Checkboxes
        checkbox_hlayout = QtWidgets.QHBoxLayout()
        checkbox_hlayout.setContentsMargins(5, 0, 5, 0)
        self.save_file = QtWidgets.QCheckBox(text="Save")
        self.use_default = QtWidgets.QCheckBox(text="Use Default")
        self.open_viewer = QtWidgets.QCheckBox(text="View when finished")
        checkbox_hlayout.addWidget(self.save_file)
        checkbox_hlayout.addWidget(self.use_default)
        checkbox_hlayout.addWidget(self.open_viewer)
        checkbox_hlayout.addStretch(True)
        # endregion Checkboxes

        # region Directory
        self.dir_widget = QtWidgets.QWidget()
        dir_hlayout = QtWidgets.QHBoxLayout()
        dir_hlayout.setContentsMargins(0, 0, 0, 0)
        dir_label = QtWidgets.QLabel("Directory :")
        dir_label.setFixedWidth(60)
        self.browse = QtWidgets.QPushButton("Browse")
        self.directory_path = QtWidgets.QLineEdit()
        self.directory_path.setPlaceholderText("Select a directory")
        dir_hlayout.addWidget(dir_label)
        dir_hlayout.addWidget(self.directory_path)
        dir_hlayout.addWidget(self.browse)
        self.dir_widget.setLayout(dir_hlayout)
        # endregion Directory

        # region Filename
        self.filename_widget = QtWidgets.QWidget()
        filename_hlayout = QtWidgets.QHBoxLayout()
        filename_hlayout.setContentsMargins(0, 0, 0, 0)
        filename_label = QtWidgets.QLabel("File Name :")
        filename_label.setFixedWidth(60)
        self.filename = QtWidgets.QLineEdit()
        filename_hlayout.addWidget(filename_label)
        filename_hlayout.addWidget(self.filename)
        self.filename_widget.setLayout(filename_hlayout)
        # endregion Filename

        # region Recent Playblast
        self.play_recent = QtWidgets.QPushButton("Play recent playblast")
        self.recent_menu = QtWidgets.QMenu()
        self.play_recent.setMenu(self.recent_menu)
        # endregion Recent Playblast

        self._layout.addLayout(checkbox_hlayout)
        self._layout.addWidget(self.filename_widget)
        self._layout.addWidget(self.dir_widget)
        self._layout.addWidget(self.play_recent)

        self.browse.clicked.connect(self.get_save_directory)
        self.use_default.stateChanged.connect(self.toggle_use_default)
        self.save_file.stateChanged.connect(self.toggle_save)

        # set state of save widgets
        self.toggle_save()
        self.toggle_use_default()

    def toggle_save(self):
        """Check to enable copy the playblast to a directory"""

        state = self.save_file.isChecked()
        self.use_default.setEnabled(state)

    def toggle_use_default(self):
        """Toggle if the file name and directory widgets are enabled"""

        state = self.use_default.isChecked()
        self.filename_widget.setVisible(not state)
        self.dir_widget.setVisible(not state)

    def get_save_directory(self):

        # Maya's browser return Linux based file paths to ensure Windows is
        # supported we use normpath
        browsed_path = os.path.normpath(lib.browse())
        filename = browsed_path.split(os.path.sep)[-1]
        filepath = browsed_path.split(filename)[0]

        self.directory_path.setText(filepath)
        self.filename.setText(filename)

    def create_call_playblast(self, filepath):

        if not os.path.isfile(filepath):
            raise RuntimeError("Given path '{}' "
                               "is not a file".format(filepath))

    def add_playblast(self, item):
        """
        Add an item to the previous playblast menu

        :param items: a collection of file paths of the playblast files
        :type items: list

        :return: None
        """

        if item in self.recent_playblasts:
            log.info("Item already in list")
            return

        if len(self.recent_playblasts) == 5:
            self.recent_playblasts.pop(4)

        self.recent_playblasts.insert(0, item)

        self.recent_menu.clear()
        for playblast in self.recent_playblasts:
            action = IoAction(parent=self, filepath=playblast)
            self.recent_menu.addAction(action)

    def on_playblast_finished(self, options):
        playblast_file = options['filename']
        if not playblast_file:
            return
        self.add_playblast(playblast_file)

    def get_outputs(self):
        """
        Get the output of the widget based on the user's inputs

        :return: collection of needed output values
        :rtype: dict
        """

        output = {"filename": None,
                  "viewer": self.open_viewer.isChecked()}

        use_default = self.use_default.isChecked()
        save = self.save_file.isChecked()
        # run playblast, don't copy to dir
        if not save:
            return output

        # run playblast, copy file to given directory
        # get directory from inputs
        if not use_default:
            directory = self.directory_path.text()
            filename = self.filename.text() or "playblast"
            path = os.path.join(directory, filename)
        else:
            # get directory from selected folder and given name
            path = lib.default_output()

        output["filename"] = path

        return output

    def get_inputs(self, as_preset):
        return_values = {"directory": self.directory_path.text(),
                         "name": self.filename.text(),
                         "use_default": self.use_default.isChecked(),
                         "save_file": self.save_file.isChecked(),
                         "open_finished": self.open_viewer.isChecked(),
                         "recent_playblasts": self.recent_playblasts}

        if as_preset:
            return_values["recent_playblasts"] = []

        return return_values

    def apply_inputs(self, settings):

        directory = settings.get("directory", None)
        filename = settings.get("name", None)
        use_default = settings.get("use_default", True)
        save_file = settings.get("save_file", True)
        open_finished = settings.get("open_finished", True)

        previous_playblasts = settings.get("recent_playblasts", [])

        self.filename.setText(filename)
        self.use_default.setChecked(use_default)
        self.save_file.setChecked(save_file)
        self.open_viewer.setChecked(open_finished)

        for playblast in reversed(previous_playblasts):
            self.add_playblast(playblast)

        self.directory_path.setText(directory)
<commit_msg>Added placeholder text for filename lineedit<commit_after>import os
import logging

from capture_gui.vendor.Qt import QtCore, QtWidgets
from capture_gui import plugin, lib

log = logging.getLogger("IO")

class IoAction(QtWidgets.QAction):

    def __init__(self, parent, filepath):
        super(IoAction, self).__init__(parent)

        action_label = os.path.basename(filepath)

        self.setText(action_label)
        self.setData(filepath)

        # check if file exists and disable when false
        self.setEnabled(os.path.isfile(filepath))

        # get icon from file
        info = QtCore.QFileInfo(filepath)
        icon_provider = QtWidgets.QFileIconProvider()
        self.setIcon(icon_provider.icon(info))

        self.triggered.connect(self.open_object_data)

    def open_object_data(self):
        lib.open_file(self.data())

class IoPlugin(plugin.Plugin):
    """Codec widget.

    Allows to set format, compression and quality.

    """
    id = "IO"
    label = "Save"
    section = "app"
    order = 40

    def __init__(self, parent=None):
        super(IoPlugin, self).__init__(parent=parent)

        self.recent_playblasts = list()

        self._layout = QtWidgets.QVBoxLayout()
        self._layout.setContentsMargins(0, 0, 0, 0)
        self.setLayout(self._layout)

        # region Checkboxes
        checkbox_hlayout = QtWidgets.QHBoxLayout()
        checkbox_hlayout.setContentsMargins(5, 0, 5, 0)
        self.save_file = QtWidgets.QCheckBox(text="Save")
        self.use_default = QtWidgets.QCheckBox(text="Use Default")
        self.open_viewer = QtWidgets.QCheckBox(text="View when finished")
        checkbox_hlayout.addWidget(self.save_file)
        checkbox_hlayout.addWidget(self.use_default)
        checkbox_hlayout.addWidget(self.open_viewer)
        checkbox_hlayout.addStretch(True)
        # endregion Checkboxes

        # region Directory
        self.dir_widget = QtWidgets.QWidget()
        dir_hlayout = QtWidgets.QHBoxLayout()
        dir_hlayout.setContentsMargins(0, 0, 0, 0)
        dir_label = QtWidgets.QLabel("Directory :")
        dir_label.setFixedWidth(60)
        self.browse = QtWidgets.QPushButton("Browse")
        self.directory_path = QtWidgets.QLineEdit()
        self.directory_path.setPlaceholderText("Select a directory")
        dir_hlayout.addWidget(dir_label)
        dir_hlayout.addWidget(self.directory_path)
        dir_hlayout.addWidget(self.browse)
        self.dir_widget.setLayout(dir_hlayout)
        # endregion Directory

        # region Filename
        self.filename_widget = QtWidgets.QWidget()
        filename_hlayout = QtWidgets.QHBoxLayout()
        filename_hlayout.setContentsMargins(0, 0, 0, 0)
        filename_label = QtWidgets.QLabel("File Name :")
        filename_label.setFixedWidth(60)
        self.filename = QtWidgets.QLineEdit()
        filename_hlayout.addWidget(filename_label)
        filename_hlayout.addWidget(self.filename)
        self.filename_widget.setLayout(filename_hlayout)
        self.filename.setPlaceholderText("Playblast")
        # endregion Filename

        # region Recent Playblast
        self.play_recent = QtWidgets.QPushButton("Play recent playblast")
        self.recent_menu = QtWidgets.QMenu()
        self.play_recent.setMenu(self.recent_menu)
        # endregion Recent Playblast

        self._layout.addLayout(checkbox_hlayout)
        self._layout.addWidget(self.filename_widget)
        self._layout.addWidget(self.dir_widget)
        self._layout.addWidget(self.play_recent)

        self.browse.clicked.connect(self.get_save_directory)
        self.use_default.stateChanged.connect(self.toggle_use_default)
        self.save_file.stateChanged.connect(self.toggle_save)

        # set state of save widgets
        self.toggle_save()
        self.toggle_use_default()

    def toggle_save(self):
        """Check to enable copy the playblast to a directory"""

        state = self.save_file.isChecked()
        self.use_default.setEnabled(state)

    def toggle_use_default(self):
        """Toggle if the file name and directory widgets are enabled"""

        state = self.use_default.isChecked()
        self.filename_widget.setVisible(not state)
        self.dir_widget.setVisible(not state)

    def get_save_directory(self):

        # Maya's browser return Linux based file paths to ensure Windows is
        # supported we use normpath
        browsed_path = os.path.normpath(lib.browse())
        filename = browsed_path.split(os.path.sep)[-1]
        filepath = browsed_path.split(filename)[0]

        self.directory_path.setText(filepath)
        self.filename.setText(filename)

    def create_call_playblast(self, filepath):

        if not os.path.isfile(filepath):
            raise RuntimeError("Given path '{}' "
                               "is not a file".format(filepath))

    def add_playblast(self, item):
        """
        Add an item to the previous playblast menu

        :param items: a collection of file paths of the playblast files
        :type items: list

        :return: None
        """

        if item in self.recent_playblasts:
            log.info("Item already in list")
            return

        if len(self.recent_playblasts) == 5:
            self.recent_playblasts.pop(4)

        self.recent_playblasts.insert(0, item)

        self.recent_menu.clear()
        for playblast in self.recent_playblasts:
            action = IoAction(parent=self, filepath=playblast)
            self.recent_menu.addAction(action)

    def on_playblast_finished(self, options):
        playblast_file = options['filename']
        if not playblast_file:
            return
        self.add_playblast(playblast_file)

    def get_outputs(self):
        """
        Get the output of the widget based on the user's inputs

        :return: collection of needed output values
        :rtype: dict
        """

        output = {"filename": None,
                  "viewer": self.open_viewer.isChecked()}

        use_default = self.use_default.isChecked()
        save = self.save_file.isChecked()
        # run playblast, don't copy to dir
        if not save:
            return output

        # run playblast, copy file to given directory
        # get directory from inputs
        if not use_default:
            directory = self.directory_path.text()
            filename = self.filename.text() or "playblast"
            path = os.path.join(directory, filename)
        else:
            # get directory from selected folder and given name
            path = lib.default_output()

        output["filename"] = path

        return output

    def get_inputs(self, as_preset):
        return_values = {"directory": self.directory_path.text(),
                         "name": self.filename.text(),
                         "use_default": self.use_default.isChecked(),
                         "save_file": self.save_file.isChecked(),
                         "open_finished": self.open_viewer.isChecked(),
                         "recent_playblasts": self.recent_playblasts}

        if as_preset:
            return_values["recent_playblasts"] = []

        return return_values

    def apply_inputs(self, settings):

        directory = settings.get("directory", None)
        filename = settings.get("name", None)
        use_default = settings.get("use_default", True)
        save_file = settings.get("save_file", True)
        open_finished = settings.get("open_finished", True)

        previous_playblasts = settings.get("recent_playblasts", [])

        self.filename.setText(filename)
        self.use_default.setChecked(use_default)
        self.save_file.setChecked(save_file)
        self.open_viewer.setChecked(open_finished)

        for playblast in reversed(previous_playblasts):
            self.add_playblast(playblast)

        self.directory_path.setText(directory)
<|endoftext|>