def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) if 'token' not in self.serverdata: raise ProtocolError("No API token defined under server settings") self.client_config = ClientConfig({'token': self.serverdata['token']}) self.client = Client(self.client_config) self.bot_config = BotConfig() self.bot = Bot(self.client, self.bot_config) self.bot_plugin = DiscordBotPlugin(self, self.bot, self.bot_config) self.bot.add_plugin(self.bot_plugin) #setup_logging(level='DEBUG') self._children = {} self.message_queue = queue.Queue()
def get_client(): from disco.client import ClientConfig, Client from rowboat.config import token config = ClientConfig() config.token = token return Client(config)
def disco_main(run=False): """ Creates an argument parser and parses a standard set of command line arguments, creating a new :class:`Client`. Returns ------- :class:`Client` A new Client from the provided command line arguments """ from disco.client import Client, ClientConfig from disco.bot import Bot, BotConfig from disco.util.logging import setup_logging # Parse out all our command line arguments args = parser.parse_args() # Create the base configuration object if args.config: config = ClientConfig.from_file(args.config) else: if os.path.exists('config.json'): config = ClientConfig.from_file('config.json') elif os.path.exists('config.yaml'): config = ClientConfig.from_file('config.yaml') else: config = ClientConfig() for arg_key, config_key in six.iteritems(CONFIG_OVERRIDE_MAPPING): if getattr(args, arg_key) is not None: setattr(config, config_key, getattr(args, arg_key)) # Setup the auto-sharder if args.shard_auto: from disco.gateway.sharder import AutoSharder AutoSharder(config).run() return # Setup logging based on the configured level setup_logging(level=getattr(logging, config.log_level.upper())) # Build out client object client = Client(config) # If applicable, build the bot and load plugins bot = None if args.run_bot or hasattr(config, 'bot'): bot_config = BotConfig(config.bot) if hasattr(config, 'bot') else BotConfig() if not hasattr(bot_config, 'plugins'): bot_config.plugins = args.plugin else: bot_config.plugins += args.plugin bot = Bot(client, bot_config) if run: (bot or client).run_forever() return bot or client
def start_disco(self): setup_logging(level=logging.DEBUG) self.config = ClientConfig.from_file("./secrets/discord.json") self.client = Client(self.config) self.bot_config = BotConfig(self.config.bot) self.bot = Bot(self.client, self.bot_config) self.bot.main_process_recv = self.main_process_recv return self.bot.client.run()
def run_bot(): from gevent import monkey monkey.patch_all() #patch_MessageTable() parser = argparse.ArgumentParser() parser.add_argument('--token', help="discord api auth token", default=None) parser.add_argument('--log-level', help='log level', default="INFO") if not os.path.exists(CONFIG_FILE): print("%s missing, pls fix" % CONFIG_FILE) exit() config = ClientConfig.from_file(CONFIG_FILE) args = parser.parse_args() if args.log_level: setup_logging(level=getattr(logging, args.log_level.upper())) if args.token is not None: config.token = args.token if not is_valid_token(config.token): print("the token '%s' isn't valid" % config.token) exit() client = Client(config) bot_config = BotConfig(config.bot) # get plugin files # todo: support multilevel nested plugins _, _, filenames = next(os.walk("plugins"), (None, None, [])) filenames.remove("__init__.py") # convert plugins from ayylmao.py to plugins.ayylmao filenames = ["plugins.%s" % os.path.splitext(p)[0] for p in filenames] # remove disabled plugins from plugin array filenames = [p for p in filenames if p not in config.disabled_plugins] bot_config.plugins = filenames if len(bot_config.plugins) > 0: print("discovered plugins:") for p in bot_config.plugins: print(" - %s" % p) else: print("no plugins found") bot = Bot(client, bot_config) bot.run_forever()
def __init__(self, **kwargs): self.db = redis.from_url(kwargs.get('redis_url'), decode_responses=True) discord_config = ClientConfig() discord_config.token = kwargs.get('discord_token') discord_client = Client(discord_config) self.api = discord_client.api self.log = logging.getLogger(self.__class__.__name__).info
def __init__(self): setup_logging(level=logging.WARNING) self.client_config = ClientConfig() self.client_config.token = app.config['DISCORD_BOT_TOKEN'] self.client = Client(self.client_config) self.bot = Bot(self.client) self.bot.add_plugin(RwrsBotDiscoPlugin)
def disco_main(run=False): """ Creates an argument parser and parses a standard set of command line arguments, creating a new :class:`Client`. Returns ------- :class:`Client` A new Client from the provided command line arguments """ args = parser.parse_args() from disco.client import Client, ClientConfig from disco.bot import Bot, BotConfig from disco.util.token import is_valid_token from disco.util.logging import setup_logging if os.path.exists(args.config): config = ClientConfig.from_file(args.config) else: config = ClientConfig() for k, v in six.iteritems(vars(args)): if hasattr(config, k) and v is not None: setattr(config, k, v) if not is_valid_token(config.token): print('Invalid token passed') return if args.shard_auto: from disco.gateway.sharder import AutoSharder AutoSharder(config).run() return if hasattr(config, 'logging_format'): setup_logging(level=logging.INFO, format=config.logging_format) else: setup_logging(level=logging.INFO) client = Client(config) bot = None if args.run_bot or hasattr(config, 'bot'): bot_config = BotConfig(config.bot) if hasattr(config, 'bot') else BotConfig() if not hasattr(bot_config, 'plugins'): bot_config.plugins = args.plugin else: bot_config.plugins += args.plugin bot = Bot(client, bot_config) if run: (bot or client).run_forever() return (bot or client)
def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) if 'token' not in self.serverdata: raise ProtocolError("No API token defined under server settings") client_config = ClientConfig({'token': self.serverdata['token'], 'max_reconnects': 0}) self.client = Client(client_config) bot_config = BotConfig() self.bot = Bot(self.client, bot_config) self.bot_plugin = DiscordBotPlugin(self, self.bot, bot_config) self.bot.add_plugin(self.bot_plugin) self._children = {} self.message_queue = queue.Queue() self.webhooks = {} self._message_thread = None
def __init__(self, token, channel_id): """Creates a new message in the given `channel_id`.""" super(DiscordIO, self).__init__() config = ClientConfig() config.token = token client = Client(config) self.text = self.__class__.__name__ try: self.message = client.api.channels_messages_create(channel_id, self.text) except Exception as e: tqdm_auto.write(str(e))
def run_shard(config, shard_id, pipe): setup_logging( level=logging.INFO, format='{} [%(levelname)s] %(asctime)s - %(name)s:%(lineno)d - %(message)s'.format(shard_id) ) config.shard_id = shard_id client = Client(config) bot = Bot(client, BotConfig(config.bot)) bot.sharder = GIPCProxy(bot, pipe) bot.shards = ShardHelper(config.shard_count, bot) bot.run_forever()
def _on_rpc_request(self, op, data): log.info('RPC REQUEST: %s', op) if op == 'INIT' and not self.client: config = ClientConfig() config.token = data['token'] config.state = { 'sync_guild_members': False, 'track_messages': False } self.client = Client(config) self.state = self.client.state self._bind_events() elif op == 'DISPATCH': self._on_dispatch(*data)
def __init__(self, *args, **kwargs): self.broker_url = config.BROKER_URL or DEFAULT_BROKER_URL self.redis_url = config.REDIS_URL or DEFAULT_BROKER_URL self.log = logging.getLogger('worker').info self.redis = redis.from_url(self.redis_url, decode_responses=True) self.log('Connected to redis database') discord_config = ClientConfig() discord_config.token = config.MEE6_TOKEN discord_client = Client(discord_config) self.api = discord_client.api self.listeners = [] self.plugins = []
def start_disco_listener(args, commands): config = setup_config(args) setup_logging(level=getattr(logging, config.log_level.upper())) client = Client(config) bot, bot_config = setup_bot(args, client, config) chat_commands_plugin = Plugin(bot, bot_config) register_chat_commands(chat_commands_plugin, commands) bot.add_plugin(chat_commands_plugin) (bot or client).run_forever()
def __init__(self, *args, **kwargs): self.broker_url = kwargs.get('broker_url', 'redis://localhost') self.redis_url = kwargs.get('redis_url', 'redis://localhost') self.log = logging.getLogger('worker').info self.redis = redis.from_url(self.redis_url, decode_responses=True) self.log('Connected to redis database') discord_config = ClientConfig() discord_config.token = kwargs.get('discord_token') discord_client = Client(discord_config) self.api = discord_client.api self.listeners = [] self.plugins = []
def createBot(self): # Create the base configuration object config = ClientConfig.from_file( '/home/nathan/PYTHON/WORKING-USE-THIS!!!/src-3.0/config/discordconfig.json' ) # Setup logging based on the configured level setup_logging(level=getattr(logging, config.log_level.upper())) # Build our client object self.client = Client(config) # If applicable, build the bot and load plugins bot_config = BotConfig(config.bot) self.bot = Bot(self.client, bot_config) #self.bot.add_plugin(TutPlugin, config) # This is how we would add plugins, TugPlugin would be a class self.apiClient = self.bot.client.api self.startMainBotLoop() (self.bot or self.client).run_forever()
def disco_main(): """ Creates an argument parser and parses a standard set of command line arguments, creating a new :class:`Client`. Returns ------- :class:`Client` A new Client from the provided command line arguments """ args = parser.parse_args() config = ClientConfig.from_file('config.yaml') config.token = credentials['discord_login']['beta'] if args.log_level: config.log_level = args.log_level setup_logging(level=getattr(logging, config.log_level.upper())) bot_config = BotConfig(config.bot) bot = ConnectBot(Client(config), bot_config) bot.run_forever()
def disco_main(run=False): """ Creates an argument parser and parses a standard set of command line arguments, creating a new :class:`Client`. Returns ------- :class:`Client` A new Client from the provided command line arguments """ from gevent import monkey monkey.patch_all() from disco.client import Client, ClientConfig from disco.bot import Bot, BotConfig from disco.util.token import is_valid_token from disco.util.logging import setup_logging config = ClientConfig.from_file('config.yaml') if not is_valid_token(config.token): print('Invalid token passed') return setup_logging(level='WARN') client = Client(config) bot_config = BotConfig(config.bot) bot = Bot(client, bot_config) from db import ChartingDao bot.db = ChartingDao() bot.run_forever() return bot
def load(self, config=None, state=None): super().load(config=config, state=state) conf = ClientConfig() conf.token = self.config.get('token') conf.max_reconnects = 0 self.client = Client(conf) self.client.parent = self self.client.agent = self.agent self.bot = None bot_config = BotConfig() bot_config.commands_require_mention = False bot_config.commands_prefix = self.config.get('command_prefix', '.') bot_config.levels = self.config.get('access', {}) bot_config.commands_level_getter = self.level_getter self.bot = Bot(self.client, bot_config) for unique_id, pconf in self.config.get('plugins').items(): if pconf is None: pconf = {} pconf.setdefault("_autoload", True) pconf.setdefault("_module", unique_id) if not pconf["_autoload"]: continue self.bot.add_plugin_module(pconf["_module"], pconf) #TODO: replace for _, plugin in self.bot.plugins.items(): plugin.parent = self self.bot.agent = self.agent self.bot.parent = self self.me = self.client.api.users_me_get()
class PyLinkDiscordProtocol(PyLinkNetworkCoreWithUtils): S2S_BUFSIZE = 0 def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) if 'token' not in self.serverdata: raise ProtocolError("No API token defined under server settings") client_config = ClientConfig({'token': self.serverdata['token'], 'max_reconnects': 0}) self.client = Client(client_config) bot_config = BotConfig() self.bot = Bot(self.client, bot_config) self.bot_plugin = DiscordBotPlugin(self, self.bot, bot_config) self.bot.add_plugin(self.bot_plugin) self._children = {} self.message_queue = queue.Queue() self.webhooks = {} self._message_thread = None @staticmethod def is_nick(s, nicklen=None): return True def is_channel(self, s): """Returns whether the target is a channel.""" try: chan = int(s) except (TypeError, ValueError): return False return chan in self.bot_plugin.state.channels def is_server_name(self, s): """Returns whether the string given is a valid IRC server name.""" return s in self.bot_plugin.state.guilds def is_internal_client(self, uid): """Returns whether the given client is an internal one.""" if uid == self.bot_plugin.me.id: return True return super().is_internal_client(uid) def get_friendly_name(self, entityid, caller=None): """ Returns the friendly name of a SID (the guild name), UID (the nick), or channel (the name). """ # internal PUID, handle appropriately if isinstance(entityid, str) and '@' in entityid: if entityid in self.users: return self.users[entityid].nick elif caller and entityid in caller.users: return caller.users[entityid].nick else: # Probably a server return entityid.split('@', 1)[0] if self.is_channel(entityid): return str(self.bot_plugin.state.channels[entityid]) elif entityid in self.bot_plugin.state.users: return self.bot_plugin.state.users[entityid].username elif self.is_server_name(entityid): return self.bot_plugin.state.guilds[entityid].name else: raise KeyError("Unknown entity ID %s" % str(entityid)) @staticmethod def wrap_message(source, target, text): """ STUB: returns the message text wrapped onto multiple lines. """ return [text] def _get_webhook(self, channel): """ Returns the webhook saved for the given channel, or try to create one if none exists. """ if channel.id in self.webhooks: # We've already saved this webhook wh = self.webhooks[channel.id] log.debug('discord: Using saved webhook %s (%s) for channel %s', wh.id, wh.name, channel) return wh # Generate a webhook name based off a configurable prefix and the channel ID webhook_name = '%s-%d' % (self.serverdata.get('webhook_name') or 'PyLinkRelay', channel.id) for wh in channel.get_webhooks(): if wh.name == webhook_name: # This hook matches our name self.webhooks[channel.id] = wh log.info('discord: Using existing webhook %s (%s) for channel %s', wh.id, webhook_name, channel) return wh # If we didn't find any webhooks, create a new one wh = self.webhooks[channel.id] = channel.create_webhook(name=webhook_name) log.info('discord: Created new webhook %s (%s) for channel %s', wh.id, wh.name, channel) return wh def _get_webhook_fields(self, user): """ Returns a dict of Relay substitution fields for the given User object. This attempts to find the original user via Relay if the .remote metadata field is set. The output includes all keys provided in User.get_fields(), plus the following: netname: The full network name of the network 'user' belongs to nettag: The short network tag of the network 'user' belongs to avatar: The URL to the user's avatar (str), or None if no avatar is specified """ # Try to lookup the remote user data via relay metadata if hasattr(user, 'remote'): remotenet, remoteuid = user.remote try: netobj = world.networkobjects[remotenet] user = netobj.users[remoteuid] except LookupError: netobj = user._irc fields = user.get_fields() fields['netname'] = netobj.get_full_network_name() fields['nettag'] = netobj.name default_avatar_url = self.serverdata.get('default_avatar_url') avatar = None # XXX: we'll have a more rigorous matching system later on if user.services_account in self.serverdata.get('avatars', {}): avatar_url = self.serverdata['avatars'][user.services_account] p = urllib.parse.urlparse(avatar_url) log.debug('(%s) Got raw avatar URL %s for user %s', self.name, avatar_url, user) if p.scheme == 'gravatar' and libgravatar: # gravatar:[email protected] try: g = libgravatar.Gravatar(p.path) log.debug('(%s) Using Gravatar email %s for user %s', self.name, p.path, user) avatar = g.get_image(use_ssl=True) except: log.exception('Failed to obtain Gravatar image for user %s/%s', user, p.path) elif p.scheme in ('http', 'https'): # a direct image link avatar = avatar_url else: log.warning('(%s) Unknown avatar URI %s for user %s', self.name, avatar_url, user) elif default_avatar_url: log.debug('(%s) Avatar not defined for user %s; using default avatar %s', self.name, user, default_avatar_url) avatar = default_avatar_url else: log.debug('(%s) Avatar not defined for user %s; using default webhook avatar', self.name, user) fields['avatar'] = avatar return fields MAX_MESSAGE_SIZE = 2000 def _message_builder(self): """ Discord message queue handler. Also supports virtual users via webhooks. """ def _send(sender, channel, pylink_target, message_parts): """ Wrapper to send a joined message. """ text = '\n'.join(message_parts) # Handle the case when the sender is not the PyLink client (sender != None) # For channels, use either virtual webhook users or CLIENTBOT_MESSAGE forwarding (relay_clientbot). if sender: user_fields = self._get_webhook_fields(sender) if channel.guild: # This message belongs to a channel netobj = self._children[channel.guild.id] # Note: skip webhook sending for messages that contain only spaces, as that fails with # 50006 "Cannot send an empty message" errors if netobj.serverdata.get('use_webhooks') and text.strip(): user_format = netobj.serverdata.get('webhook_user_format', "$nick @ $netname") tmpl = string.Template(user_format) webhook_fake_username = tmpl.safe_substitute(self._get_webhook_fields(sender)) try: webhook = self._get_webhook(channel) webhook.execute(content=text[:self.MAX_MESSAGE_SIZE], username=webhook_fake_username, avatar_url=user_fields['avatar']) except APIException as e: if e.code == 10015 and channel.id in self.webhooks: log.info("(%s) Invalidating webhook %s for channel %s due to Unknown Webhook error (10015)", self.name, self.webhooks[channel.id], channel) del self.webhooks[channel.id] elif e.code == 50013: # Prevent spamming errors: disable webhooks we don't have the right permissions log.warning("(%s) Disabling webhooks on guild %s/%s due to insufficient permissions (50013). Rehash to re-enable.", self.name, channel.guild.id, channel.guild.name) self.serverdata.update( {'guilds': {channel.guild.id: {'use_webhooks': False} } }) else: log.error("(%s) Caught API exception when sending webhook message to channel %s: %s/%s", self.name, channel, e.response.status_code, e.code) log.debug("(%s) APIException full traceback:", self.name, exc_info=True) except: log.exception("(%s) Failed to send webhook message to channel %s", self.name, channel) else: return for line in message_parts: netobj.call_hooks([sender.uid, 'CLIENTBOT_MESSAGE', {'target': pylink_target, 'text': line}]) return else: # This is a forwarded PM - prefix the message with its sender info. pm_format = self.serverdata.get('pm_format', "Message from $nick @ $netname: $text") user_fields['text'] = text text = string.Template(pm_format).safe_substitute(user_fields) try: channel.send_message(text[:self.MAX_MESSAGE_SIZE]) except Exception as e: log.exception("(%s) Could not send message to channel %s (pylink_target=%s)", self.name, channel, pylink_target) joined_messages = collections.defaultdict(collections.deque) while not self._aborted.is_set(): try: # message is an instance of QueuedMessage (defined in this file) message = self.message_queue.get(timeout=BATCH_DELAY) message.text = utils.strip_irc_formatting(message.text) if not self.serverdata.get('allow_mention_everyone', False): message.text = message.text.replace('@here', '@ here') message.text = message.text.replace('@everyone', '@ everyone') # First, buffer messages by channel joined_messages[message.channel].append(message) except queue.Empty: # Then process them together when we run out of things in the queue for channel, messages in joined_messages.items(): next_message = [] length = 0 current_sender = None # We group messages here to avoid being throttled as often. In short, we want to send a message when: # 1) The virtual sender (for webhook purposes) changes # 2) We reach the message limit for one batch (2000 chars) # 3) We run out of messages at the end while messages: message = messages.popleft() next_message.append(message.text) length += len(message.text) if message.sender != current_sender or length >= self.MAX_MESSAGE_SIZE: current_sender = message.sender _send(current_sender, channel, message.pylink_target, next_message) next_message.clear() length = 0 # The last batch if next_message: _send(current_sender, channel, message.pylink_target, next_message) joined_messages.clear() except Exception: log.exception("Exception in message queueing thread:") def _create_child(self, server_id, guild_name): """ Creates a virtual network object for a server with the given name. """ # This is a bit different because we let the child server find its own name # and report back to us. child = DiscordServer(None, self, server_id, guild_name) world.networkobjects[child.name] = self._children[server_id] = child return child def _remove_child(self, server_id): """ Removes a virtual network object with the given name. """ pylink_netobj = self._children[server_id] pylink_netobj.call_hooks([None, 'PYLINK_DISCONNECT', {}]) del self._children[server_id] del world.networkobjects[pylink_netobj.name] def connect(self): self._aborted.clear() self._message_thread = threading.Thread(name="Messaging thread for %s" % self.name, target=self._message_builder, daemon=True) self._message_thread.start() self.client.run() def disconnect(self): """Disconnects from Discord and shuts down this network object.""" self._aborted.set() self._pre_disconnect() children = self._children.copy() for child in children: self._remove_child(child) self.client.gw.shutting_down = True self.client.gw.ws.close() self._post_disconnect()
def setUp(self): self.client = Client(ClientConfig({'config': 'TEST_TOKEN'})) self.bot = MockBot(self.client)
def disco_main(run=False): """ Creates an argument parser and parses a standard set of command line arguments, creating a new :class:`Client`. Returns ------- :class:`Client` A new Client from the provided command line arguments """ from disco.client import Client, ClientConfig from disco.bot import Bot, BotConfig from disco.util.logging import setup_logging, LOG_FORMAT from bot.base import bot args = bot.local.disco # Create the base configuration object if args.config: config = ClientConfig.from_file(args.config) else: config = ClientConfig(args.to_dict()) for arg_key, config_key in six.iteritems(CONFIG_OVERRIDE_MAPPING): if getattr(args, arg_key) is not None: setattr(config, config_key, getattr(args, arg_key)) # Setup the auto-sharder if args.shard_auto: from disco.gateway.sharder import AutoSharder AutoSharder(config).run() return # Setup logging based on the configured level if not os.path.exists("logs"): os.makedirs("logs") file_handler = logging.FileHandler("logs/bot.log") file_handler.setFormatter(logging.Formatter(LOG_FORMAT)) file_handler.setLevel(config.log_level.upper()) stream_handler = logging.StreamHandler() setup_logging( handlers=(file_handler, stream_handler), level=getattr(logging, config.log_level.upper()), ) # Build out client object client = Client(config) # If applicable, build the bot and load plugins bot = None if args.run_bot or hasattr(config, 'bot'): bot_config = BotConfig(config.bot.to_dict()) if hasattr( config, 'bot') else BotConfig() if not hasattr(bot_config, 'plugins'): bot_config.plugins = args.plugin else: bot_config.plugins += args.plugin bot = Bot(client, bot_config) if run: (bot or client).run_forever() return (bot or client)
class PyLinkDiscordProtocol(PyLinkNetworkCoreWithUtils): def __init__(self, *args, **kwargs): from gevent import monkey monkey.patch_all() super().__init__(*args, **kwargs) self._hooks_queue = queue.Queue() if 'token' not in self.serverdata: raise ProtocolError("No API token defined under server settings") self.client_config = ClientConfig({'token': self.serverdata['token']}) self.client = Client(self.client_config) self.bot_config = BotConfig() self.bot = Bot(self.client, self.bot_config) self.bot_plugin = DiscordBotPlugin(self, self.bot, self.bot_config) self.bot.add_plugin(self.bot_plugin) setup_logging(level='DEBUG') self._children = {} self.message_queue = queue.Queue() def _message_builder(self): current_channel_senders = {} joined_messages = defaultdict(dict) while not self._aborted.is_set(): try: message = self.message_queue.get(timeout=0.1) message_text = message.pop('text', '') channel = message.pop('target') current_sender = current_channel_senders.get(channel, None) if current_sender != message['sender']: self.flush(channel, joined_messages[channel]) joined_messages[channel] = message current_channel_senders[channel] = message['sender'] joined_message = joined_messages[channel].get('text', '') joined_messages[channel]['text'] = joined_message + "\n{}".format(message_text) except queue.Empty: for channel, message_info in joined_messages.items(): self.flush(channel, message_info) joined_messages = defaultdict(dict) current_channel_senders = {} def flush(self, channel, message_info): message_text = message_info.pop('text', '').strip() if message_text: if message_info.get('username'): message_info['webhook'].execute( content=message_text, username=message_info['username'], avatar_url=message_info.get('avatar'), ) else: channel.send_message(message_text) def _process_hooks(self): """Loop to process incoming hook data.""" while not self._aborted.is_set(): data = self._hooks_queue.get() if data is None: log.debug('(%s) Stopping queue thread due to getting None as item', self.name) break elif self not in world.networkobjects.values(): log.debug('(%s) Stopping stale queue thread; no longer matches world.networkobjects', self.name) break subserver, data = data if subserver not in world.networkobjects: log.error('(%s) Not queuing hook for subserver %r no longer in networks list.', self.name, subserver) elif subserver in self._children: self._children[subserver].call_hooks(data) def _add_hook(self, subserver, data): """ Pushes a hook payload for the given subserver. """ if subserver not in self._children: raise ValueError("Unknown subserver %s" % subserver) self._hooks_queue.put_nowait(( subserver, data )) def _create_child(self, name, server_id): """ Creates a virtual network object for a server with the given name. """ if name in world.networkobjects: raise ValueError("Attempting to reintroduce network with name %r" % name) child = DiscordServer(name, self, server_id) world.networkobjects[name] = self._children[name] = child return child def _remove_child(self, name): """ Removes a virtual network object with the given name. """ self._add_hook(name, [None, 'PYLINK_DISCONNECT', {}]) del self._children[name] del world.networkobjects[name] def connect(self): self._aborted.clear() self._queue_thread = threading.Thread(name="Queue thread for %s" % self.name, target=self._process_hooks, daemon=True) self._queue_thread.start() self._message_thread = threading.Thread(name="Message thread for %s" % self.name, target=self._message_builder, daemon=True) self._message_thread.start() self.client.run() def websocket_close(self, *_, **__): return self.disconnect() def disconnect(self): self._aborted.set() self._pre_disconnect() log.debug('(%s) Killing hooks handler', self.name) try: # XXX: queue.Queue.queue isn't actually documented, so this is probably not reliable in the long run. with self._hooks_queue.mutex: self._hooks_queue.queue[0] = None except IndexError: self._hooks_queue.put(None) children = self._children.copy() for child in children: self._remove_child(child) if world.shutting_down.is_set(): self.bot.client.gw.shutting_down = True log.debug('(%s) Sending Discord logout', self.name) self.bot.client.gw.session_id = None self.bot.client.gw.ws.close() self._post_disconnect()
class PyLinkDiscordProtocol(PyLinkNetworkCoreWithUtils): S2S_BUFSIZE = 0 def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) if 'token' not in self.serverdata: raise ProtocolError("No API token defined under server settings") self.client_config = ClientConfig({'token': self.serverdata['token']}) self.client = Client(self.client_config) self.bot_config = BotConfig() self.bot = Bot(self.client, self.bot_config) self.bot_plugin = DiscordBotPlugin(self, self.bot, self.bot_config) self.bot.add_plugin(self.bot_plugin) #setup_logging(level='DEBUG') self._children = {} self.message_queue = queue.Queue() @staticmethod def is_nick(s, nicklen=None): return True def is_channel(self, s): """Returns whether the target is a channel.""" try: chan = int(s) except (TypeError, ValueError): return False return chan in self.bot_plugin.state.channels def is_server_name(self, s): """Returns whether the string given is a valid IRC server name.""" return s in self.bot_plugin.state.guilds def get_friendly_name(self, entityid, caller=None): """ Returns the friendly name of a SID (the guild name), UID (the nick), or channel (the name). """ # internal PUID, handle appropriately if isinstance(entityid, str) and '@' in entityid: if entityid in self.users: return self.users[entityid].nick elif caller and entityid in caller.users: return caller.users[entityid].nick else: # Probably a server return entityid.split('@', 1)[0] if self.is_channel(entityid): return str(self.bot_plugin.state.channels[entityid]) elif entityid in self.bot_plugin.state.users: return self.bot_plugin.state.users[entityid].username elif self.is_server_name(entityid): return self.bot_plugin.state.guilds[entityid].name else: raise KeyError("Unknown entity ID %s" % str(entityid)) @staticmethod def wrap_message(source, target, text): """ STUB: returns the message text wrapped onto multiple lines. """ return [text] def _message_builder(self): current_channel_senders = {} joined_messages = collections.defaultdict(dict) while not self._aborted.is_set(): try: message = self.message_queue.get(timeout=BATCH_DELAY) message_text = message.pop('text', '') channel = message.pop('target') current_sender = current_channel_senders.get(channel, None) # We'll enable this when we work on webhook support again... #if current_sender != message['sender']: # self.flush(channel, joined_messages[channel]) # joined_messages[channel] = message current_channel_senders[channel] = message['sender'] joined_message = joined_messages[channel].get('text', '') joined_messages[channel][ 'text'] = joined_message + "\n{}".format(message_text) except queue.Empty: for channel, message_info in joined_messages.items(): self.flush(channel, message_info) joined_messages.clear() current_channel_senders.clear() def flush(self, channel, message_info): message_text = message_info.pop('text', '').strip() if message_text: if message_info.get('username'): message_info['webhook'].execute( content=message_text, username=message_info['username'], avatar_url=message_info.get('avatar'), ) else: channel.send_message(message_text) def _create_child(self, server_id, guild_name): """ Creates a virtual network object for a server with the given name. """ # Try to find a predefined server name; if that fails, use the server id. # We don't use the guild name here because those can be changed at any time, # confusing plugins that store data by PyLink network names. fallback_name = 'd%d' % server_id name = self.serverdata.get('server_names', {}).get(server_id, fallback_name) if name in world.networkobjects: raise ValueError("Attempting to reintroduce network with name %r" % name) child = DiscordServer(name, self, server_id, guild_name) world.networkobjects[name] = self._children[server_id] = child return child def _remove_child(self, server_id): """ Removes a virtual network object with the given name. """ pylink_netobj = self._children[server_id] pylink_netobj.call_hooks([None, 'PYLINK_DISCONNECT', {}]) del self._children[server_id] del world.networkobjects[pylink_netobj.name] def connect(self): self._aborted.clear() self.client.run() def disconnect(self): """Disconnects from Discord and shuts down this network object.""" self._aborted.set() self._pre_disconnect() children = self._children.copy() for child in children: self._remove_child(child) self.client.gw.shutting_down = True self.client.gw.ws.close() self._post_disconnect()
from gevent.monkey import patch_all; patch_all() import logging from os import environ from disco.bot import Bot, BotConfig from disco.client import Client, ClientConfig from disco.util.logging import setup_logging setup_logging(level=logging.INFO) config = ClientConfig.from_file("config.json") config.token = environ['token'] client = Client(config) bot_config = BotConfig(config.bot) bot = Bot(client, bot_config) if __name__ == '__main__': bot.run_forever()
def disco_main(run=False): """ Creates an argument parser and parses a standard set of command line arguments, creating a new :class:`Client`. Returns ------- :class:`Client` A new Client from the provided command line arguments """ args = parser.parse_args() from disco.client import Client, ClientConfig from disco.bot import Bot, BotConfig from disco.util.token import is_valid_token from disco.util.logging import setup_logging if os.path.exists(args.config): config = ClientConfig.from_file(args.config) else: config = ClientConfig() config.manhole_enable = args.manhole if args.manhole_bind: config.manhole_bind = args.manhole_bind for k, v in six.iteritems(vars(args)): if hasattr(config, k) and v is not None: setattr(config, k, v) if not is_valid_token(config.token): print('Invalid token passed') return if args.shard_auto: from disco.gateway.sharder import AutoSharder AutoSharder(config).run() return # TODO: make configurable setup_logging(level=getattr(logging, args.log_level.upper())) client = Client(config) bot = None if args.run_bot or hasattr(config, 'bot'): bot_config = BotConfig(config.bot) if hasattr(config, 'bot') else BotConfig() if not hasattr(bot_config, 'plugins'): bot_config.plugins = args.plugin else: bot_config.plugins += args.plugin if args.http_bind: bot_config.http_enabled = True host, port = args.http_bind.split(':', 1) bot_config.http_host = host bot_config.http_port = int(port) bot = Bot(client, bot_config) if run: (bot or client).run_forever() return (bot or client)
from disco.client import ClientConfig, Client, APIClient from disco.types.channel import MessageIterator from disco.types.permissions import Permissions from datetime import datetime from util.encoders import MessageEncoder from util.toCSV import toCSVLine with open('./config.json') as file: #Pull config variables from file data = json.load(file) AUTH_TOKEN = data['token'] DEBUG = data['debug'] mConfig = ClientConfig() mConfig.token = AUTH_TOKEN mClient = Client(mConfig) class ArchivePlugin(Plugin): @Plugin.command('ping') def command_ping(self, event): event.msg.reply('Pong!') @Plugin.command('archive', parser=True) @Plugin.parser.add_argument('--JSON', help="Archive in JSON format", action="store_true", default=False) @Plugin.parser.add_argument('--CSV', help="Archive in CSV format", action="store_true",
def get_client(): from disco.client import ClientConfig, Client config = ClientConfig() config.token = bot_config.token return Client(config)