modernize module system: 5.0.0
This commit is contained in:
parent
5b68793f4f
commit
d55e111767
|
@ -6,8 +6,8 @@ Quick start
|
||||||
-----------
|
-----------
|
||||||
|
|
||||||
* Install: `python3 setup.py install`
|
* Install: `python3 setup.py install`
|
||||||
* Configure: `cd examples ; vim config.json data/config/Services.json`
|
* Configure: `vim examples/config.json examples/data/config/Services.json`
|
||||||
* Run: `pyircbot -c config.json`
|
* Run: `pyircbot -c examples/config.json`
|
||||||
|
|
||||||
Running in docker
|
Running in docker
|
||||||
-----------------
|
-----------------
|
||||||
|
|
|
@ -0,0 +1,131 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import logging
|
||||||
|
from contextlib import closing
|
||||||
|
from argparse import ArgumentParser
|
||||||
|
from json import loads, load
|
||||||
|
from msgbus.client import MsgbusSubClient
|
||||||
|
import pyircbot
|
||||||
|
import traceback
|
||||||
|
from pyircbot.pyircbot import PrimitiveBot
|
||||||
|
from pyircbot.irccore import IRCEvent, UserPrefix
|
||||||
|
from json import dumps
|
||||||
|
|
||||||
|
|
||||||
|
# IRCEvent = namedtuple("IRCEvent", "command args prefix trailing")
|
||||||
|
# UserPrefix = namedtuple("UserPrefix", "nick username hostname")
|
||||||
|
# ServerPrefix = namedtuple("ServerPrefix", "hostname")
|
||||||
|
|
||||||
|
|
||||||
|
class PyIRCBotSub(PrimitiveBot):
|
||||||
|
def __init__(self, name, host, port, config):
|
||||||
|
super().__init__(config)
|
||||||
|
self.name = name
|
||||||
|
self.host = host
|
||||||
|
self.port = port
|
||||||
|
self.client = None # PubSub socket
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
# Connect to msgbus and loop through messages
|
||||||
|
with closing(MsgbusSubClient(self.host, self.port)) as self.client:
|
||||||
|
self.client.sub("pyircbot_privmsg")#TODO More of these
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
channel, body = self.client.recv()
|
||||||
|
self.process_line(channel, body)
|
||||||
|
except Exception as e:
|
||||||
|
traceback.print_exc()
|
||||||
|
|
||||||
|
def process_line(self, channel, body):
|
||||||
|
name, rest = body.split(" ", 1)
|
||||||
|
if name != self.name:
|
||||||
|
return
|
||||||
|
|
||||||
|
# pyircbot_privmsg default
|
||||||
|
# [["#jesusandhacking"], "xMopxShell", "test", {"prefix": ["xMopxShell", "~xMopxShel", "192.95.23.134"]}]
|
||||||
|
args, sender, trailing, extras = loads(rest)
|
||||||
|
nick, username, hostname = extras["prefix"]
|
||||||
|
|
||||||
|
msg = IRCEvent("PRIVMSG",
|
||||||
|
args,
|
||||||
|
UserPrefix(nick,
|
||||||
|
username,
|
||||||
|
hostname),
|
||||||
|
trailing)
|
||||||
|
|
||||||
|
for module_name, module in self.moduleInstances.items():
|
||||||
|
for hook in module.irchooks:
|
||||||
|
validation = hook.validator(msg, self)
|
||||||
|
if validation:
|
||||||
|
hook.method(msg, validation)
|
||||||
|
|
||||||
|
# client.pub("pyircbot_send", "default privmsg {}".format(dumps([channel, "{}: pong".format(sender)])))
|
||||||
|
|
||||||
|
" Filesystem Methods "
|
||||||
|
def getConfigPath(self, moduleName):
|
||||||
|
"""Return the absolute path for a module's config file
|
||||||
|
|
||||||
|
:param moduleName: the module who's config file we want
|
||||||
|
:type moduleName: str"""
|
||||||
|
|
||||||
|
basepath = "%s/config/%s" % (self.botconfig["bot"]["datadir"], moduleName)
|
||||||
|
|
||||||
|
if os.path.exists("%s.json" % basepath):
|
||||||
|
return "%s.json" % basepath
|
||||||
|
return None
|
||||||
|
|
||||||
|
def getDataPath(self, moduleName):
|
||||||
|
"""Return the absolute path for a module's data dir
|
||||||
|
|
||||||
|
:param moduleName: the module who's data dir we want
|
||||||
|
:type moduleName: str"""
|
||||||
|
module_dir = os.path.join(self.botconfig["bot"]["datadir"], "data", moduleName)
|
||||||
|
if not os.path.exists(module_dir):
|
||||||
|
os.mkdir(module_dir)
|
||||||
|
return module_dir
|
||||||
|
|
||||||
|
" IRC methods "
|
||||||
|
def act_PRIVMSG(self, towho, message):
|
||||||
|
"""Use the `/msg` command
|
||||||
|
|
||||||
|
:param towho: the target #channel or user's name
|
||||||
|
:type towho: str
|
||||||
|
:param message: the message to send
|
||||||
|
:type message: str"""
|
||||||
|
# self.sendRaw("PRIVMSG %s :%s" % (towho, message))
|
||||||
|
self.client.pub("pyircbot_send", "{} {} {}".format(self.name, "privmsg", dumps([towho, message])))
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
logging.basicConfig(level=logging.INFO,
|
||||||
|
format="%(asctime)-15s %(levelname)-8s %(filename)s:%(lineno)d %(message)s")
|
||||||
|
log = logging.getLogger('main')
|
||||||
|
|
||||||
|
# parse command line args
|
||||||
|
parser = ArgumentParser(description="Run pyircbot plugins behind a pubsub client")
|
||||||
|
parser.add_argument("-c", "--config", help="Pyircbot config file")
|
||||||
|
parser.add_argument("-s", "--server", default="localhost", help="Msgbus server address")
|
||||||
|
parser.add_argument("-p", "--port", default=7100, type=int, help="Msgbus server port")
|
||||||
|
parser.add_argument("-n", "--name", default="default", help="bot name")
|
||||||
|
parser.add_argument("--debug", action="store_true", help="")
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
if args.debug:
|
||||||
|
logging.getLogger().setLevel(logging.DEBUG)
|
||||||
|
log.debug(args)
|
||||||
|
|
||||||
|
# Load config
|
||||||
|
with open(args.config) as f:
|
||||||
|
config = load(f)
|
||||||
|
|
||||||
|
bot = PyIRCBotSub(args.name, args.server, int(args.port), config)
|
||||||
|
|
||||||
|
# Load modules in config
|
||||||
|
sys.path.append(os.path.dirname(pyircbot.__file__) + "/modules/")
|
||||||
|
for modulename in config["modules"]:
|
||||||
|
print("Load: ", modulename)
|
||||||
|
bot.loadmodule(modulename)
|
||||||
|
|
||||||
|
bot.run()
|
||||||
|
|
|
@ -3,6 +3,7 @@ import sys
|
||||||
import logging
|
import logging
|
||||||
import signal
|
import signal
|
||||||
from argparse import ArgumentParser
|
from argparse import ArgumentParser
|
||||||
|
from pyircbot.common import load
|
||||||
from pyircbot import PyIRCBot
|
from pyircbot import PyIRCBot
|
||||||
from json import loads
|
from json import loads
|
||||||
|
|
||||||
|
@ -30,7 +31,7 @@ if __name__ == "__main__":
|
||||||
log.critical("No bot config file specified (-c). Exiting.")
|
log.critical("No bot config file specified (-c). Exiting.")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
botconfig = loads(sys.stdin.read()) if args.config == "-" else PyIRCBot.load(args.config)
|
botconfig = loads(sys.stdin.read()) if args.config == "-" else load(args.config)
|
||||||
|
|
||||||
log.debug(botconfig)
|
log.debug(botconfig)
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,7 @@ Contents:
|
||||||
modules/_modules.rst
|
modules/_modules.rst
|
||||||
rpc/_rpc.rst
|
rpc/_rpc.rst
|
||||||
module_guide/_module_guide.rst
|
module_guide/_module_guide.rst
|
||||||
|
module_guide/_pubsub_mode.rst
|
||||||
changelog.rst
|
changelog.rst
|
||||||
|
|
||||||
More Information
|
More Information
|
||||||
|
|
|
@ -74,6 +74,8 @@ Prefix may also be a ``ServerPrefix`` object, if the hook is for an IRC method
|
||||||
that interacts with the server directly, such as PING. It would have the
|
that interacts with the server directly, such as PING. It would have the
|
||||||
properties ``event.prefix.hostname`` and ``event.prefix.str``.
|
properties ``event.prefix.hostname`` and ``event.prefix.str``.
|
||||||
|
|
||||||
|
There are more hook-like decorators. See @regex and @command.
|
||||||
|
|
||||||
Since the module described above echos messages, let's do that:
|
Since the module described above echos messages, let's do that:
|
||||||
|
|
||||||
.. code-block:: python
|
.. code-block:: python
|
||||||
|
@ -134,30 +136,6 @@ In usage:
|
||||||
4:40:17 PM <Beefpile> test
|
4:40:17 PM <Beefpile> test
|
||||||
4:40:17 PM <derpbot420> test
|
4:40:17 PM <derpbot420> test
|
||||||
|
|
||||||
New Style Module Hooks
|
|
||||||
----------------------
|
|
||||||
|
|
||||||
Instead of receiving the values of the IRC event a module is responding to in
|
|
||||||
3 separate arguments, hooks can receive them as one object. The hook system
|
|
||||||
will automatically determine which argument style to use.
|
|
||||||
|
|
||||||
The reason for this change is to eliminate some unnecessary code in modules.
|
|
||||||
Any module that looks at a user's nick or hostname may find itself doing
|
|
||||||
something like this in every hook:
|
|
||||||
|
|
||||||
.. code-block:: python
|
|
||||||
|
|
||||||
def saynick(self, args, prefix, trailing):
|
|
||||||
prefixObj = self.bot.decodePrefix(prefix)
|
|
||||||
self.bot.act_PRIVMSG(args[0], "Hello, %s. You are connecting from %s" % (prefixObj.nick, prefixObj.hostname))
|
|
||||||
|
|
||||||
With the new style, one line can be eliminated, as the passed ``IRCEvent``
|
|
||||||
event has the prefix already parsed:
|
|
||||||
|
|
||||||
.. code-block:: python
|
|
||||||
|
|
||||||
def saynick(self, event):
|
|
||||||
self.bot.act_PRIVMSG(event.args[0], "Hello, %s. You are connecting from %s" % (event.prefix.nick, event.prefix.hostname))
|
|
||||||
|
|
||||||
Advanced Usage
|
Advanced Usage
|
||||||
==============
|
==============
|
||||||
|
|
|
@ -0,0 +1,31 @@
|
||||||
|
***********
|
||||||
|
PubSub Mode
|
||||||
|
***********
|
||||||
|
|
||||||
|
The PubSubClient module provides a ZeroMQ-based PubSub remote control capability to pyircbot. Well, what if you could
|
||||||
|
also run pyircbot modules as a client of the pubsub bus? Look no further!
|
||||||
|
|
||||||
|
Obviously, the :py:class:`pyircbot.modules.PubSubClient.PubSubClient` module should be enabled. Take note of the `name`
|
||||||
|
parameter (which may be left at the default "default").
|
||||||
|
|
||||||
|
The so-called footless (as opposed to headless, as the bot's head is the IRC client connection) client needs only a
|
||||||
|
small set of bootstrap modules:
|
||||||
|
|
||||||
|
- PingResponder (:py:class:`pyircbot.modules.PingResponder.PingResponder`)
|
||||||
|
- Services (:py:class:`pyircbot.modules.Services.Services`)
|
||||||
|
- PubSubClient (:py:class:`pyircbot.modules.PubSubClient.PubSubClient`)
|
||||||
|
|
||||||
|
Launch the bot and let it connect to the msgbus server.
|
||||||
|
|
||||||
|
Next, create a config identical to that of a normal pyircbot, but with any modules desired enabled. Also, read the
|
||||||
|
`--help` text for the `pubsubbot` program. Launch the `pubsubbot` process:
|
||||||
|
|
||||||
|
.. code-block:: shell
|
||||||
|
|
||||||
|
pubsubbot -c config-sub.json --name <name>
|
||||||
|
|
||||||
|
After connecting to the message bus, `pubsubbot` will be hosting any configured modules. It can be exited and restarted
|
||||||
|
any time without affecting the IRC client connection!
|
||||||
|
|
||||||
|
For a module to support running in this mode, it must use only methods described in the Module Developers Guide. Using
|
||||||
|
APIs outside of this - while not discouraged - will definitely break compatibility.
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"bot":{
|
"bot":{
|
||||||
"datadir":"./data/",
|
"datadir":"./examples/data/",
|
||||||
"rpcbind":"0.0.0.0",
|
"rpcbind":"0.0.0.0",
|
||||||
"rpcport":1876,
|
"rpcport":1876,
|
||||||
"usermodules": [ "./data/modules/" ]
|
"usermodules": [ "./data/modules/" ]
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
{
|
{
|
||||||
"limit": 25,
|
"limit": 25,
|
||||||
"recv_msg": "Oh, thanks, I'll keep %(adjective)s%(item)s safe",
|
"recv_msg": "Oh, thanks, I'll keep %(adjective)s%(item)s safe",
|
||||||
"inv_msg": "\u0000\u0001ACTION is carrying %(itemlist)s\u0000\u0001",
|
"inv_msg": "\u0001ACTION is carrying %(itemlist)s\u0001",
|
||||||
"swap_msg": "\u0000\u0001ACTION takes %(adjective)s%(recv_item)s but drops %(drop_item)s\u0000\u0010",
|
"swap_msg": "\u0001ACTION takes %(adjective)s%(recv_item)s but drops %(drop_item)s\u0010",
|
||||||
"dupe_msg": "No thanks, I've already got %(item)s",
|
"dupe_msg": "No thanks, I've already got %(item)s",
|
||||||
"adjectives": [
|
"adjectives": [
|
||||||
"some",
|
"some",
|
||||||
|
@ -10,4 +10,4 @@
|
||||||
"an",
|
"an",
|
||||||
"these"
|
"these"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,2 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
pyircbot --config config.json
|
|
|
@ -1,5 +1,10 @@
|
||||||
from time import time
|
from time import time
|
||||||
from math import floor
|
from math import floor
|
||||||
|
from json import load as json_load
|
||||||
|
from collections import namedtuple
|
||||||
|
|
||||||
|
|
||||||
|
ParsedCommand = namedtuple("ParsedCommand", "command args args_str message")
|
||||||
|
|
||||||
|
|
||||||
class burstbucket(object):
|
class burstbucket(object):
|
||||||
|
@ -39,3 +44,63 @@ class burstbucket(object):
|
||||||
self.bucket -= 1
|
self.bucket -= 1
|
||||||
return 0
|
return 0
|
||||||
return self.bucket_period - since_fill
|
return self.bucket_period - since_fill
|
||||||
|
|
||||||
|
|
||||||
|
def messageHasCommand(command, message, requireArgs=False):
|
||||||
|
"""Check if a message has a command with or without args in it
|
||||||
|
|
||||||
|
:param command: the command string to look for, like !ban. If a list is passed, the first match is returned.
|
||||||
|
:type command: str or list
|
||||||
|
:param message: the message string to look in, like "!ban Lil_Mac"
|
||||||
|
:type message: str
|
||||||
|
:param requireArgs: if true, only validate if the command use has any amount of trailing text
|
||||||
|
:type requireArgs: bool"""
|
||||||
|
|
||||||
|
if not type(command) == list:
|
||||||
|
command = [command]
|
||||||
|
for item in command:
|
||||||
|
cmd = messageHasCommandSingle(item, message, requireArgs)
|
||||||
|
if cmd:
|
||||||
|
return cmd
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def messageHasCommandSingle(command, message, requireArgs=False):
|
||||||
|
# Check if the message at least starts with the command
|
||||||
|
messageBeginning = message[0:len(command)]
|
||||||
|
if messageBeginning != command:
|
||||||
|
return False
|
||||||
|
# Make sure it's not a subset of a longer command (ie .meme being set off by .memes)
|
||||||
|
subsetCheck = message[len(command):len(command) + 1]
|
||||||
|
if subsetCheck != " " and subsetCheck != "":
|
||||||
|
return False
|
||||||
|
|
||||||
|
# We've got the command! Do we need args?
|
||||||
|
argsStart = len(command)
|
||||||
|
args = ""
|
||||||
|
if argsStart > 0:
|
||||||
|
args = message[argsStart + 1:].strip()
|
||||||
|
|
||||||
|
if requireArgs and args == '':
|
||||||
|
return False
|
||||||
|
|
||||||
|
# Verified! Return the set.
|
||||||
|
return ParsedCommand(command,
|
||||||
|
args.split(" ") if args else [],
|
||||||
|
args,
|
||||||
|
message)
|
||||||
|
|
||||||
|
|
||||||
|
def load(filepath):
|
||||||
|
"""Return an object from the passed filepath
|
||||||
|
|
||||||
|
:param filepath: path to a json file. filename must end with .json
|
||||||
|
:type filepath: str
|
||||||
|
:Returns: | dict
|
||||||
|
"""
|
||||||
|
|
||||||
|
if filepath.endswith(".json"):
|
||||||
|
with open(filepath, 'r') as f:
|
||||||
|
return json_load(f)
|
||||||
|
else:
|
||||||
|
raise Exception("Unknown config format")
|
||||||
|
|
|
@ -172,6 +172,7 @@ class IRCCore(object):
|
||||||
def initHooks(self):
|
def initHooks(self):
|
||||||
"""Defines hooks that modules can listen for events of"""
|
"""Defines hooks that modules can listen for events of"""
|
||||||
self.hooks = [
|
self.hooks = [
|
||||||
|
'_ALL',
|
||||||
'_CONNECT',
|
'_CONNECT',
|
||||||
'_DISCONNECT',
|
'_DISCONNECT',
|
||||||
'_RECV',
|
'_RECV',
|
||||||
|
@ -222,7 +223,7 @@ class IRCCore(object):
|
||||||
:type prefix: str
|
:type prefix: str
|
||||||
:param trailing: data payload of the command
|
:param trailing: data payload of the command
|
||||||
:type trailing: str"""
|
:type trailing: str"""
|
||||||
for hook in self.hookcalls[command]:
|
for hook in self.hookcalls["_ALL"] + self.hookcalls[command]:
|
||||||
try:
|
try:
|
||||||
if len(getargspec(hook).args) == 2:
|
if len(getargspec(hook).args) == 2:
|
||||||
hook(IRCCore.packetAsObject(command, args, prefix, trailing))
|
hook(IRCCore.packetAsObject(command, args, prefix, trailing))
|
||||||
|
|
|
@ -9,7 +9,8 @@
|
||||||
import re
|
import re
|
||||||
import os
|
import os
|
||||||
import logging
|
import logging
|
||||||
from .pyircbot import PyIRCBot
|
from .common import load as pload
|
||||||
|
from .common import messageHasCommand
|
||||||
|
|
||||||
|
|
||||||
class ModuleBase:
|
class ModuleBase:
|
||||||
|
@ -28,9 +29,6 @@ class ModuleBase:
|
||||||
self.bot = bot
|
self.bot = bot
|
||||||
"""Reference to the master PyIRCBot object"""
|
"""Reference to the master PyIRCBot object"""
|
||||||
|
|
||||||
self.hooks = []
|
|
||||||
"""Low-level protocol hooks this module has"""
|
|
||||||
|
|
||||||
self.irchooks = []
|
self.irchooks = []
|
||||||
"""IRC Hooks this module has"""
|
"""IRC Hooks this module has"""
|
||||||
|
|
||||||
|
@ -73,7 +71,7 @@ class ModuleBase:
|
||||||
if not self.config:
|
if not self.config:
|
||||||
configPath = self.getConfigPath()
|
configPath = self.getConfigPath()
|
||||||
if configPath is not None:
|
if configPath is not None:
|
||||||
self.config = PyIRCBot.load(configPath)
|
self.config = pload(configPath)
|
||||||
|
|
||||||
def onenable(self):
|
def onenable(self):
|
||||||
"""Called when the module is enabled"""
|
"""Called when the module is enabled"""
|
||||||
|
@ -228,7 +226,7 @@ class command(hook):
|
||||||
"""
|
"""
|
||||||
if not super().validate(msg, bot):
|
if not super().validate(msg, bot):
|
||||||
return False
|
return False
|
||||||
if not self.allow_private and msg.args[0] == "#":
|
if msg.args[0][0] != "#" and not self.allow_private:
|
||||||
return False
|
return False
|
||||||
for keyword in self.keywords:
|
for keyword in self.keywords:
|
||||||
single = self._validate_one(msg, keyword)
|
single = self._validate_one(msg, keyword)
|
||||||
|
@ -238,7 +236,7 @@ class command(hook):
|
||||||
|
|
||||||
def _validate_one(self, msg, keyword):
|
def _validate_one(self, msg, keyword):
|
||||||
with_prefix = "{}{}".format(self.prefix, keyword)
|
with_prefix = "{}{}".format(self.prefix, keyword)
|
||||||
return PyIRCBot.messageHasCommand(with_prefix, msg.trailing, requireArgs=self.require_args)
|
return messageHasCommand(with_prefix, msg.trailing, requireArgs=self.require_args)
|
||||||
|
|
||||||
|
|
||||||
class regex(hook):
|
class regex(hook):
|
||||||
|
|
|
@ -13,7 +13,6 @@ from pyircbot.modulebase import ModuleBase
|
||||||
class AttributeStorage(ModuleBase):
|
class AttributeStorage(ModuleBase):
|
||||||
def __init__(self, bot, moduleName):
|
def __init__(self, bot, moduleName):
|
||||||
ModuleBase.__init__(self, bot, moduleName)
|
ModuleBase.__init__(self, bot, moduleName)
|
||||||
self.hooks = []
|
|
||||||
self.services = ["attributes"]
|
self.services = ["attributes"]
|
||||||
self.db = None
|
self.db = None
|
||||||
serviceProviders = self.bot.getmodulesbyservice("mysql")
|
serviceProviders = self.bot.getmodulesbyservice("mysql")
|
||||||
|
|
|
@ -13,7 +13,6 @@ from pyircbot.modulebase import ModuleBase
|
||||||
class AttributeStorageLite(ModuleBase):
|
class AttributeStorageLite(ModuleBase):
|
||||||
def __init__(self, bot, moduleName):
|
def __init__(self, bot, moduleName):
|
||||||
ModuleBase.__init__(self, bot, moduleName)
|
ModuleBase.__init__(self, bot, moduleName)
|
||||||
self.hooks = []
|
|
||||||
self.services = ["attributes"]
|
self.services = ["attributes"]
|
||||||
self.db = None
|
self.db = None
|
||||||
serviceProviders = self.bot.getmodulesbyservice("sqlite")
|
serviceProviders = self.bot.getmodulesbyservice("sqlite")
|
||||||
|
|
|
@ -6,7 +6,9 @@
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from pyircbot.modulebase import ModuleBase, ModuleHook
|
from pyircbot.modulebase import ModuleBase, command
|
||||||
|
from pyircbot.modules.ModInfo import info
|
||||||
|
from decimal import Decimal
|
||||||
from requests import get
|
from requests import get
|
||||||
from time import time
|
from time import time
|
||||||
|
|
||||||
|
@ -18,25 +20,23 @@ class BitcoinPrice(ModuleBase):
|
||||||
self.cache = None
|
self.cache = None
|
||||||
self.cacheAge = 0
|
self.cacheAge = 0
|
||||||
|
|
||||||
self.hooks = [
|
@info("btc retrieve the current price of bitcoin", cmds=["btc"])
|
||||||
ModuleHook(["PRIVMSG"], self.btc)
|
@command("btc", "bitcoin")
|
||||||
]
|
def btc(self, msg, cmd):
|
||||||
|
replyTo = msg.prefix.nick if "#" not in msg.args[0] else msg.args[0]
|
||||||
|
|
||||||
def btc(self, args, prefix, trailing):
|
data = self.getApi()
|
||||||
prefix = self.bot.decodePrefix(prefix)
|
self.bot.act_PRIVMSG(replyTo, "%s: %s" % (
|
||||||
replyTo = prefix.nick if "#" not in args[0] else args[0]
|
msg.prefix.nick,
|
||||||
|
"\x02\x0307Bitcoin:\x03\x02 \x0307${price:.2f}\x0f - "
|
||||||
cmd = self.bot.messageHasCommand([".btc", ".bitcoin"], trailing)
|
"24h change: \x0307${change:.2f}\x0f - "
|
||||||
if cmd:
|
"24h volume: \x0307${volume:.0f}M\x0f".format(price=Decimal(data["price_usd"]),
|
||||||
data = self.getApi()
|
change=Decimal(data["percent_change_24h"]),
|
||||||
self.bot.act_PRIVMSG(replyTo, "%s: %s" % (
|
volume=Decimal(data["24h_volume_usd"]) / 10**6)
|
||||||
prefix.nick,
|
))
|
||||||
"\x02\x0307Bitcoin:\x03\x02 \x0307{buy:.0f}\x0f$ - High: \x0307{high:.0f}\x0f$ - "
|
|
||||||
"Low: \x0307{low:.0f}\x0f$ - Volume: \x0307{vol_cur:.0f}\x03฿".format(**data['ticker'])
|
|
||||||
))
|
|
||||||
|
|
||||||
def getApi(self):
|
def getApi(self):
|
||||||
if self.cache is None or time() - self.cacheAge > self.config["cache"]:
|
if self.cache is None or time() - self.cacheAge > self.config["cache"]:
|
||||||
self.cache = get("https://btc-e.com/api/2/btc_usd/ticker").json()
|
self.cache = get("https://api.coinmarketcap.com/v1/ticker/bitcoin/").json()[0]
|
||||||
self.cacheAge = time()
|
self.cacheAge = time()
|
||||||
return self.cache
|
return self.cache
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
|
|
||||||
from pyircbot.modulebase import ModuleBase, ModuleHook, MissingDependancyException, regex, command
|
from pyircbot.modulebase import ModuleBase, MissingDependancyException, regex, command
|
||||||
from pyircbot.modules.ModInfo import info
|
from pyircbot.modules.ModInfo import info
|
||||||
import datetime
|
import datetime
|
||||||
import time
|
import time
|
||||||
|
@ -93,12 +93,13 @@ class Calc(ModuleBase):
|
||||||
|
|
||||||
if word and changeit:
|
if word and changeit:
|
||||||
# Add a new calc or delete
|
# Add a new calc or delete
|
||||||
if self.config["allowDelete"] and not value:
|
if not value:
|
||||||
result = self.deleteCalc(channel, word)
|
if self.config["allowDelete"]:
|
||||||
if result:
|
result = self.deleteCalc(channel, word)
|
||||||
self.bot.act_PRIVMSG(channel, "Calc deleted, %s." % sender)
|
if result:
|
||||||
else:
|
self.bot.act_PRIVMSG(channel, "Calc deleted, %s." % sender)
|
||||||
self.bot.act_PRIVMSG(channel, "Sorry %s, I don't know what '%s' is." % (sender, word))
|
else:
|
||||||
|
self.bot.act_PRIVMSG(channel, "Sorry %s, I don't know what '%s' is." % (sender, word))
|
||||||
else:
|
else:
|
||||||
if self.config["delaySubmit"] > 0 and self.timeSince(channel, "add") < self.config["delaySubmit"]:
|
if self.config["delaySubmit"] > 0 and self.timeSince(channel, "add") < self.config["delaySubmit"]:
|
||||||
self.bot.act_PRIVMSG(channel, self.remainingToStr(self.config["delaySubmit"],
|
self.bot.act_PRIVMSG(channel, self.remainingToStr(self.config["delaySubmit"],
|
||||||
|
|
|
@ -15,7 +15,6 @@ from threading import Thread
|
||||||
class CryptoWalletRPC(ModuleBase):
|
class CryptoWalletRPC(ModuleBase):
|
||||||
def __init__(self, bot, moduleName):
|
def __init__(self, bot, moduleName):
|
||||||
ModuleBase.__init__(self, bot, moduleName)
|
ModuleBase.__init__(self, bot, moduleName)
|
||||||
self.hooks = []
|
|
||||||
self.services = ["bitcoinrpc"]
|
self.services = ["bitcoinrpc"]
|
||||||
self.rpcservices = {}
|
self.rpcservices = {}
|
||||||
self.loadrpcservices()
|
self.loadrpcservices()
|
||||||
|
|
|
@ -14,7 +14,6 @@ from bitcoinrpc.authproxy import AuthServiceProxy
|
||||||
class DogeRPC(ModuleBase):
|
class DogeRPC(ModuleBase):
|
||||||
def __init__(self, bot, moduleName):
|
def __init__(self, bot, moduleName):
|
||||||
ModuleBase.__init__(self, bot, moduleName)
|
ModuleBase.__init__(self, bot, moduleName)
|
||||||
self.hooks = []
|
|
||||||
self.services = ["dogerpc"]
|
self.services = ["dogerpc"]
|
||||||
self.rpc = DogeController(self)
|
self.rpc = DogeController(self)
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,8 @@
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from pyircbot.modulebase import ModuleBase, ModuleHook
|
from pyircbot.modulebase import ModuleBase, command
|
||||||
|
from pyircbot.modules.ModInfo import info
|
||||||
import time
|
import time
|
||||||
import json
|
import json
|
||||||
import random
|
import random
|
||||||
|
@ -18,7 +19,6 @@ import os
|
||||||
class DuckHunt(ModuleBase):
|
class DuckHunt(ModuleBase):
|
||||||
def __init__(self, bot, moduleName):
|
def __init__(self, bot, moduleName):
|
||||||
ModuleBase.__init__(self, bot, moduleName)
|
ModuleBase.__init__(self, bot, moduleName)
|
||||||
self.hooks = [ModuleHook("PRIVMSG", self.hunt)]
|
|
||||||
|
|
||||||
self.jsonPath = self.getFilePath("scores.json")
|
self.jsonPath = self.getFilePath("scores.json")
|
||||||
|
|
||||||
|
@ -29,109 +29,103 @@ class DuckHunt(ModuleBase):
|
||||||
|
|
||||||
self.startHunt()
|
self.startHunt()
|
||||||
|
|
||||||
def hunt(self, args, prefix, trailing):
|
@info("huntscore show your duckhunt score", cmds=["huntscore"])
|
||||||
prefixObj = self.bot.decodePrefix(prefix)
|
@command("huntscore", allow_private=True)
|
||||||
fromWho = prefixObj.nick
|
def hunt(self, msg, cmd):
|
||||||
|
scores = self.loadScores()
|
||||||
|
fromWho = msg.prefix.nick
|
||||||
|
if fromWho not in scores:
|
||||||
|
self.bot.act_PRIVMSG(fromWho, "You have no points :(")
|
||||||
|
else:
|
||||||
|
scores = scores[fromWho]
|
||||||
|
kills = 0
|
||||||
|
runts = 0
|
||||||
|
prime = 0
|
||||||
|
weight = 0.0
|
||||||
|
shots = 0
|
||||||
|
misses = 0
|
||||||
|
for kill in scores:
|
||||||
|
if kill["prime"]:
|
||||||
|
prime += 1
|
||||||
|
if kill["runt"]:
|
||||||
|
runts += 1
|
||||||
|
kills += 1
|
||||||
|
weight += kill["weight"]
|
||||||
|
shots += 1
|
||||||
|
shots += kill["misses"]
|
||||||
|
misses += kill["misses"]
|
||||||
|
|
||||||
cmd = self.bot.messageHasCommand("!huntscore", trailing, False)
|
self.bot.act_PRIVMSG(fromWho, "You've shot %s %s for a total weight of %s lbs." %
|
||||||
if cmd:
|
(kills, self.config["animalSpeciesPlural"], weight))
|
||||||
scores = self.loadScores()
|
self.bot.act_PRIVMSG(fromWho, "%s prime catches, %s runts, %s bullets used and %s misses." %
|
||||||
if fromWho not in scores:
|
(prime, runts, shots, misses))
|
||||||
self.bot.act_PRIVMSG(fromWho, "You have no points :(")
|
# self.bot.act_PRIVMSG(fromWho, "More info & highscores: http://duckhunt.xmopx.net/")
|
||||||
|
|
||||||
|
@info("shoot shoot active targets", cmds=["shoot"])
|
||||||
|
@command("shoot")
|
||||||
|
def cmd_shoot(self, msg, args):
|
||||||
|
if self.isDuckOut:
|
||||||
|
fromWho = msg.prefix.nick
|
||||||
|
|
||||||
|
if fromWho not in self.misses:
|
||||||
|
self.misses[fromWho] = 0
|
||||||
|
|
||||||
|
shotIn = round(time.time() - self.outStart, 2)
|
||||||
|
|
||||||
|
if random.randint(0, 100) <= self.config["missChance"]:
|
||||||
|
self.bot.act_PRIVMSG(self.config["activeChannel"], "%s fires after %s seconds and misses!" %
|
||||||
|
(fromWho, shotIn))
|
||||||
|
self.misses[fromWho] += 1
|
||||||
|
return
|
||||||
|
|
||||||
|
self.isDuckOut = False
|
||||||
|
|
||||||
|
bagged = {
|
||||||
|
"species": self.config["animalSpecies"],
|
||||||
|
"gender": "M" if random.randint(0, 1) == 1 else "F",
|
||||||
|
"time": shotIn,
|
||||||
|
"prime": False,
|
||||||
|
"runt": False,
|
||||||
|
"weight": 0.0,
|
||||||
|
"date": time.time(),
|
||||||
|
"misses": self.misses[fromWho]
|
||||||
|
}
|
||||||
|
|
||||||
|
message = "%s %s " % (fromWho, "bags")
|
||||||
|
|
||||||
|
if random.randint(0, 100) <= self.config["primeChance"]:
|
||||||
|
bagged["prime"] = True
|
||||||
|
bagged["weight"] = self.getRandWeight(self.config["weightMax"], self.config["weightFat"])
|
||||||
|
message += "a prime catch, a "
|
||||||
|
elif random.randint(0, 100) <= self.config["runtChance"]:
|
||||||
|
bagged["runt"] = True
|
||||||
|
bagged["weight"] = self.getRandWeight(self.config["weightRunt"], self.config["weightMin"])
|
||||||
|
message += "a runt of a catch, a "
|
||||||
else:
|
else:
|
||||||
scores = scores[fromWho]
|
bagged["weight"] = self.getRandWeight(self.config["weightMin"], self.config["weightMax"])
|
||||||
kills = 0
|
message += "a "
|
||||||
runts = 0
|
|
||||||
prime = 0
|
|
||||||
weight = 0.0
|
|
||||||
shots = 0
|
|
||||||
misses = 0
|
|
||||||
for kill in scores:
|
|
||||||
if kill["prime"]:
|
|
||||||
prime += 1
|
|
||||||
if kill["runt"]:
|
|
||||||
runts += 1
|
|
||||||
kills += 1
|
|
||||||
weight += kill["weight"]
|
|
||||||
shots += 1
|
|
||||||
shots += kill["misses"]
|
|
||||||
misses += kill["misses"]
|
|
||||||
|
|
||||||
self.bot.act_PRIVMSG(fromWho, "You've shot %s %s for a total weight of %s lbs." %
|
message += "%s lb " % (bagged["weight"])
|
||||||
(kills, self.config["animalSpeciesPlural"], weight))
|
if bagged["gender"] == "M":
|
||||||
self.bot.act_PRIVMSG(fromWho, "%s prime catches, %s runts, %s bullets used and %s misses." %
|
message += self.config["animalNameMale"] + " "
|
||||||
(prime, runts, shots, misses))
|
else:
|
||||||
# self.bot.act_PRIVMSG(fromWho, "More info & highscores: http://duckhunt.xmopx.net/")
|
message += self.config["animalNameFemale"] + " "
|
||||||
self.log.info("DuckHunt: %s used !huntscore" % fromWho)
|
|
||||||
return
|
|
||||||
|
|
||||||
# Channel only
|
message += "in %s seconds!" % shotIn
|
||||||
if not args[0][0] == "#":
|
self.bot.act_PRIVMSG(self.config["activeChannel"], message)
|
||||||
return
|
|
||||||
|
|
||||||
cmd = self.bot.messageHasCommand("!shoot", trailing, False)
|
self.addKillFor(fromWho, bagged)
|
||||||
if cmd:
|
|
||||||
if self.isDuckOut:
|
|
||||||
|
|
||||||
if fromWho not in self.misses:
|
self.misses = {}
|
||||||
self.misses[fromWho] = 0
|
|
||||||
|
|
||||||
shotIn = round(time.time() - self.outStart, 2)
|
self.startHunt()
|
||||||
|
|
||||||
if random.randint(0, 100) <= self.config["missChance"]:
|
|
||||||
self.bot.act_PRIVMSG(self.config["activeChannel"], "%s fires after %s seconds and misses!" %
|
|
||||||
(fromWho, shotIn))
|
|
||||||
self.misses[fromWho] += 1
|
|
||||||
return
|
|
||||||
|
|
||||||
self.isDuckOut = False
|
|
||||||
|
|
||||||
bagged = {
|
|
||||||
"species": self.config["animalSpecies"],
|
|
||||||
"gender": "M" if random.randint(0, 1) == 1 else "F",
|
|
||||||
"time": shotIn,
|
|
||||||
"prime": False,
|
|
||||||
"runt": False,
|
|
||||||
"weight": 0.0,
|
|
||||||
"date": time.time(),
|
|
||||||
"misses": self.misses[fromWho]
|
|
||||||
}
|
|
||||||
|
|
||||||
message = "%s %s " % (fromWho, "bags")
|
|
||||||
|
|
||||||
if random.randint(0, 100) <= self.config["primeChance"]:
|
|
||||||
bagged["prime"] = True
|
|
||||||
bagged["weight"] = self.getRandWeight(self.config["weightMax"], self.config["weightFat"])
|
|
||||||
message += "a prime catch, a "
|
|
||||||
elif random.randint(0, 100) <= self.config["runtChance"]:
|
|
||||||
bagged["runt"] = True
|
|
||||||
bagged["weight"] = self.getRandWeight(self.config["weightRunt"], self.config["weightMin"])
|
|
||||||
message += "a runt of a catch, a "
|
|
||||||
else:
|
|
||||||
bagged["weight"] = self.getRandWeight(self.config["weightMin"], self.config["weightMax"])
|
|
||||||
message += "a "
|
|
||||||
|
|
||||||
message += "%s lb " % (bagged["weight"])
|
|
||||||
if bagged["gender"] == "M":
|
|
||||||
message += self.config["animalNameMale"] + " "
|
|
||||||
else:
|
|
||||||
message += self.config["animalNameFemale"] + " "
|
|
||||||
|
|
||||||
message += "in %s seconds!" % shotIn
|
|
||||||
self.bot.act_PRIVMSG(self.config["activeChannel"], message)
|
|
||||||
|
|
||||||
self.addKillFor(fromWho, bagged)
|
|
||||||
|
|
||||||
self.misses = {}
|
|
||||||
|
|
||||||
self.startHunt()
|
|
||||||
|
|
||||||
def startHunt(self):
|
def startHunt(self):
|
||||||
" Creates a timer that waits a certain amount of time then sends out a bird \\_o< quack"
|
" Creates a timer that waits a certain amount of time then sends out a bird \\_o< quack"
|
||||||
delay = self.config["delayMin"] + random.randint(0, self.config["delayMax"] - self.config["delayMin"])
|
delay = self.config["delayMin"] + random.randint(0, self.config["delayMax"] - self.config["delayMin"])
|
||||||
self.timer = Timer(delay, self.duckOut)
|
self.timer = Timer(delay, self.duckOut)
|
||||||
self.timer.start()
|
self.timer.start()
|
||||||
print("DuckHunt: Sending out animal in %s seconds" % delay)
|
self.log.info(" Sending out animal in %s seconds" % delay)
|
||||||
|
|
||||||
def duckOut(self):
|
def duckOut(self):
|
||||||
self.isDuckOut = True
|
self.isDuckOut = True
|
||||||
|
@ -143,12 +137,6 @@ class DuckHunt(ModuleBase):
|
||||||
weight = float(weight) * random.random()
|
weight = float(weight) * random.random()
|
||||||
return round(weight + minW, 2)
|
return round(weight + minW, 2)
|
||||||
|
|
||||||
def getScoreFor(self, playername):
|
|
||||||
return 1
|
|
||||||
|
|
||||||
def getScoresFor(self, playername):
|
|
||||||
return 1
|
|
||||||
|
|
||||||
def addKillFor(self, playername, kill):
|
def addKillFor(self, playername, kill):
|
||||||
scores = self.loadScores()
|
scores = self.loadScores()
|
||||||
if scores is None:
|
if scores is None:
|
||||||
|
|
|
@ -7,14 +7,14 @@
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from pyircbot.modulebase import ModuleBase, ModuleHook
|
from pyircbot.modulebase import ModuleBase, command
|
||||||
|
from pyircbot.modules.ModInfo import info
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
|
|
||||||
class Inventory(ModuleBase):
|
class Inventory(ModuleBase):
|
||||||
def __init__(self, bot, moduleName):
|
def __init__(self, bot, moduleName):
|
||||||
ModuleBase.__init__(self, bot, moduleName)
|
ModuleBase.__init__(self, bot, moduleName)
|
||||||
self.hooks = []
|
|
||||||
self.db = None
|
self.db = None
|
||||||
serviceProviders = self.bot.getmodulesbyservice("sqlite")
|
serviceProviders = self.bot.getmodulesbyservice("sqlite")
|
||||||
if not serviceProviders:
|
if not serviceProviders:
|
||||||
|
@ -33,42 +33,38 @@ class Inventory(ModuleBase):
|
||||||
) ;""")
|
) ;""")
|
||||||
c.close()
|
c.close()
|
||||||
|
|
||||||
self.hooks = [ModuleHook("PRIVMSG", self.checkInv)]
|
@info("have <item> give the bot an item", cmds=["have"])
|
||||||
|
@command("have")
|
||||||
def checkInv(self, args, prefix, trailing):
|
def checkInv(self, msg, cmd):
|
||||||
if not args[0][0] == "#":
|
if len(cmd.args) < 1:
|
||||||
return
|
return
|
||||||
prefixObj = self.bot.decodePrefix(prefix)
|
adjective = None
|
||||||
cmd = self.bot.messageHasCommand([".have"], trailing, True)
|
newItem = cmd.args_str
|
||||||
if cmd:
|
if cmd.args[0] in self.config["adjectives"]:
|
||||||
if len(cmd.args) < 1:
|
newItem = cmd.args_str[len(cmd.args[0]):].strip()
|
||||||
return
|
adjective = cmd.args[0]
|
||||||
adjective = None
|
|
||||||
if cmd.args[0] in self.config["adjectives"]:
|
|
||||||
cmd.args_str = cmd.args_str[len(cmd.args[0]):].strip()
|
|
||||||
adjective = cmd.args[0]
|
|
||||||
newItem = cmd.args_str
|
|
||||||
|
|
||||||
if self.has_item(newItem):
|
if self.has_item(newItem):
|
||||||
self.bot.act_PRIVMSG(args[0], self.config["dupe_msg"] % {"item": newItem})
|
self.bot.act_PRIVMSG(msg.args[0], self.config["dupe_msg"] % {"item": newItem})
|
||||||
return
|
return
|
||||||
|
|
||||||
dropped = self.add_item(prefixObj.nick, newItem)
|
dropped = self.add_item(msg.prefix.nick, newItem)
|
||||||
if len(dropped) > 0:
|
if len(dropped) > 0:
|
||||||
self.bot.act_PRIVMSG(args[0], self.config["swap_msg"] %
|
self.bot.act_PRIVMSG(msg.args[0], self.config["swap_msg"] %
|
||||||
{"adjective": (adjective + " ") if adjective else "",
|
{"adjective": (adjective + " ") if adjective else "",
|
||||||
"recv_item": newItem, "drop_item": ", ".join(dropped)})
|
"recv_item": newItem, "drop_item": ", ".join(dropped)})
|
||||||
else:
|
else:
|
||||||
self.bot.act_PRIVMSG(args[0], self.config["recv_msg"] %
|
self.bot.act_PRIVMSG(msg.args[0], self.config["recv_msg"] %
|
||||||
{"item": newItem, "adjective": "these " if newItem[-1:] == "s" else "this "})
|
{"item": newItem, "adjective": "these " if newItem[-1:] == "s" else "this "})
|
||||||
|
|
||||||
cmd = self.bot.messageHasCommand([".inventory", ".inv"], trailing)
|
@info("inventory show the bot's inventory", cmds=["inventory", "inv"])
|
||||||
if cmd:
|
@command("inventory", "inv")
|
||||||
inv = self.getinventory()
|
def cmd_inv(self, msg, cmd):
|
||||||
if len(inv) == 0:
|
inv = self.getinventory()
|
||||||
self.bot.act_PRIVMSG(args[0], self.config["inv_msg"] % {"itemlist": "nothing!"})
|
if len(inv) == 0:
|
||||||
else:
|
self.bot.act_PRIVMSG(msg.args[0], self.config["inv_msg"] % {"itemlist": "nothing!"})
|
||||||
self.bot.act_PRIVMSG(args[0], self.config["inv_msg"] % {"itemlist": ", ".join(inv)})
|
else:
|
||||||
|
self.bot.act_PRIVMSG(msg.args[0], self.config["inv_msg"] % {"itemlist": ", ".join(inv)})
|
||||||
|
|
||||||
def has_item(self, itemName):
|
def has_item(self, itemName):
|
||||||
c = self.db.query("SELECT COUNT(*) as `num` FROM `inventory` WHERE `item`=? COLLATE NOCASE", (itemName,))
|
c = self.db.query("SELECT COUNT(*) as `num` FROM `inventory` WHERE `item`=? COLLATE NOCASE", (itemName,))
|
||||||
|
|
|
@ -7,24 +7,20 @@
|
||||||
"""
|
"""
|
||||||
|
|
||||||
import urllib.parse
|
import urllib.parse
|
||||||
from pyircbot.modulebase import ModuleBase, ModuleHook
|
from pyircbot.modulebase import ModuleBase, command
|
||||||
|
from pyircbot.modules.ModInfo import info
|
||||||
|
|
||||||
BASE_URL = "http://lmgtfy.com/?q="
|
BASE_URL = "http://lmgtfy.com/?q="
|
||||||
|
|
||||||
|
|
||||||
class LMGTFY(ModuleBase):
|
class LMGTFY(ModuleBase):
|
||||||
def __init__(self, bot, moduleName):
|
|
||||||
ModuleBase.__init__(self, bot, moduleName)
|
|
||||||
self.hooks.append(ModuleHook("PRIVMSG", self.handleMessage))
|
|
||||||
self.bot = bot
|
|
||||||
|
|
||||||
def handleMessage(self, args, prefix, trailing):
|
@info("lmgtfy <term> display a condescending internet query", cmds=["lmgtfy"])
|
||||||
channel = args[0]
|
@command("lmgtfy", require_args=True)
|
||||||
prefix = self.bot.decodePrefix(prefix)
|
def handleMessage(self, msg, cmd):
|
||||||
if self.bot.messageHasCommand(".lmgtfy", trailing):
|
message = msg.trailing.split(" ")[1:]
|
||||||
message = trailing.split(" ")[1:]
|
link = self.createLink(message)
|
||||||
link = self.createLink(message)
|
self.bot.act_PRIVMSG(msg.args[0], "%s: %s" % (msg.prefix.nick, link))
|
||||||
self.bot.act_PRIVMSG(channel, "%s: %s" % (prefix.nick, link))
|
|
||||||
|
|
||||||
def createLink(self, message):
|
def createLink(self, message):
|
||||||
finalUrl = BASE_URL
|
finalUrl = BASE_URL
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from pyircbot.modulebase import ModuleBase, ModuleHook
|
from pyircbot.modulebase import ModuleBase, hook
|
||||||
from requests import get
|
from requests import get
|
||||||
import re
|
import re
|
||||||
import time
|
import time
|
||||||
|
@ -22,16 +22,14 @@ class LinkTitler(ModuleBase):
|
||||||
def __init__(self, bot, moduleName):
|
def __init__(self, bot, moduleName):
|
||||||
ModuleBase.__init__(self, bot, moduleName)
|
ModuleBase.__init__(self, bot, moduleName)
|
||||||
self.REQUEST_SIZE_LIMIT = 10 * 1024
|
self.REQUEST_SIZE_LIMIT = 10 * 1024
|
||||||
self.hooks = [ModuleHook("PRIVMSG", self.searches)]
|
|
||||||
|
|
||||||
def searches(self, args, prefix, trailing):
|
@hook("PRIVMSG")
|
||||||
t = Thread(target=self.doLinkTitle, args=(args, prefix, trailing))
|
def searches(self, msg, cmd):
|
||||||
|
t = Thread(target=self.doLinkTitle, args=(msg.args, msg.prefix.nick, msg.trailing))
|
||||||
t.daemon = True
|
t.daemon = True
|
||||||
t.start()
|
t.start()
|
||||||
|
|
||||||
def doLinkTitle(self, args, prefix, trailing):
|
def doLinkTitle(self, args, sender, trailing):
|
||||||
sender = self.bot.decodePrefix(prefix)
|
|
||||||
|
|
||||||
# Youtube
|
# Youtube
|
||||||
matches = re.compile(r'(?:youtube.*?(?:v=|/v/)|youtu\.be/|yooouuutuuube.*?id=)([-_a-z0-9]+)', re.I) \
|
matches = re.compile(r'(?:youtube.*?(?:v=|/v/)|youtu\.be/|yooouuutuuube.*?id=)([-_a-z0-9]+)', re.I) \
|
||||||
.findall(trailing)
|
.findall(trailing)
|
||||||
|
@ -67,7 +65,7 @@ class LinkTitler(ModuleBase):
|
||||||
"domain": submission.domain,
|
"domain": submission.domain,
|
||||||
"nsfw": "[NSFW]" if submission.over_18 else "",
|
"nsfw": "[NSFW]" if submission.over_18 else "",
|
||||||
"points": submission.ups,
|
"points": submission.ups,
|
||||||
"percent": "%s%%" % int(submission.upvote_ratio *100),
|
"percent": "%s%%" % int(submission.upvote_ratio * 100),
|
||||||
"comments": submission.num_comments,
|
"comments": submission.num_comments,
|
||||||
"author": submission.author.name,
|
"author": submission.author.name,
|
||||||
"date": datetime.datetime.fromtimestamp(submission.created).strftime("%Y.%m.%d")
|
"date": datetime.datetime.fromtimestamp(submission.created).strftime("%Y.%m.%d")
|
||||||
|
@ -98,11 +96,11 @@ class LinkTitler(ModuleBase):
|
||||||
# Fetch HTML title
|
# Fetch HTML title
|
||||||
title = self.url_htmltitle(match[0])
|
title = self.url_htmltitle(match[0])
|
||||||
if title:
|
if title:
|
||||||
self.bot.act_PRIVMSG(args[0], "%s: \x02%s\x02" % (sender.nick, title))
|
self.bot.act_PRIVMSG(args[0], "%s: \x02%s\x02" % (sender, title))
|
||||||
else:
|
else:
|
||||||
# Unknown types, just print type and size
|
# Unknown types, just print type and size
|
||||||
self.bot.act_PRIVMSG(args[0], "%s: \x02%s\x02, %s" %
|
self.bot.act_PRIVMSG(args[0], "%s: \x02%s\x02, %s" %
|
||||||
(sender.nick, headers["Content-Type"],
|
(sender, headers["Content-Type"],
|
||||||
self.nicesize(int(headers["Content-Length"])) if
|
self.nicesize(int(headers["Content-Length"])) if
|
||||||
"Content-Length" in headers else "unknown size"))
|
"Content-Length" in headers else "unknown size"))
|
||||||
|
|
||||||
|
|
|
@ -34,8 +34,16 @@ class info(object):
|
||||||
self.commands = cmds or []
|
self.commands = cmds or []
|
||||||
|
|
||||||
def __call__(self, func):
|
def __call__(self, func):
|
||||||
setattr(func, "irchelp", self.docstring)
|
if hasattr(func, "irchelp"):
|
||||||
setattr(func, "irchelpc", self.commands)
|
func.irchelp.append(self.docstring)
|
||||||
|
else:
|
||||||
|
setattr(func, "irchelp", [self.docstring])
|
||||||
|
|
||||||
|
if hasattr(func, "irchelpc"):
|
||||||
|
func.irchelpc.append(self.commands)
|
||||||
|
else:
|
||||||
|
setattr(func, "irchelpc", [self.commands])
|
||||||
|
|
||||||
return func
|
return func
|
||||||
|
|
||||||
|
|
||||||
|
@ -77,5 +85,6 @@ class ModInfo(ModuleBase):
|
||||||
for attr_name in dir(module):
|
for attr_name in dir(module):
|
||||||
attr = getattr(module, attr_name)
|
attr = getattr(module, attr_name)
|
||||||
if callable(attr) and hasattr(attr, "irchelp"):
|
if callable(attr) and hasattr(attr, "irchelp"):
|
||||||
yield (modname, module, getattr(attr, "irchelp"), getattr(attr, "irchelpc"), )
|
for cmdhelp, cmdaliases in zip(getattr(attr, "irchelp"), getattr(attr, "irchelpc")):
|
||||||
|
yield (modname, module, cmdhelp, cmdaliases, )
|
||||||
raise StopIteration()
|
raise StopIteration()
|
||||||
|
|
|
@ -19,7 +19,6 @@ except:
|
||||||
class MySQL(ModuleBase):
|
class MySQL(ModuleBase):
|
||||||
def __init__(self, bot, moduleName):
|
def __init__(self, bot, moduleName):
|
||||||
ModuleBase.__init__(self, bot, moduleName)
|
ModuleBase.__init__(self, bot, moduleName)
|
||||||
self.hooks = []
|
|
||||||
self.services = ["mysql"]
|
self.services = ["mysql"]
|
||||||
self.connection = self.getConnection()
|
self.connection = self.getConnection()
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,8 @@
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from pyircbot.modulebase import ModuleBase, ModuleHook
|
from pyircbot.modulebase import ModuleBase, command
|
||||||
|
from pyircbot.modules.ModInfo import info
|
||||||
from time import time
|
from time import time
|
||||||
from requests import get
|
from requests import get
|
||||||
from lxml import etree
|
from lxml import etree
|
||||||
|
@ -19,112 +20,107 @@ class NFLLive(ModuleBase):
|
||||||
self.cache = None
|
self.cache = None
|
||||||
self.cacheAge = 0
|
self.cacheAge = 0
|
||||||
|
|
||||||
self.hooks = [ModuleHook(["PRIVMSG"], self.nflitup)]
|
@info("nfl show nfl schedule & score", cmds=["nfl"])
|
||||||
|
@command("nfl")
|
||||||
|
def nflitup(self, message, cmd):
|
||||||
|
games = self.getNflGamesCached()
|
||||||
|
msg = []
|
||||||
|
|
||||||
def nflitup(self, args, prefix, trailing):
|
liveGames = []
|
||||||
prefix = self.bot.decodePrefix(prefix)
|
gamesLaterToday = []
|
||||||
replyTo = prefix.nick if "#" not in args[0] else args[0]
|
gamesToday = []
|
||||||
|
gamesUpcoming = []
|
||||||
|
gamesEarlierWeek = []
|
||||||
|
|
||||||
cmd = self.bot.messageHasCommand(".nfl", trailing)
|
# sort games
|
||||||
if cmd:
|
for game in games["games"]:
|
||||||
games = self.getNflGamesCached()
|
if game["time"] is not None:
|
||||||
msg = []
|
liveGames.append(game)
|
||||||
|
elif game["quarter"] == "P" and game["startdate"].day == datetime.now().day:
|
||||||
liveGames = []
|
gamesLaterToday.append(game)
|
||||||
gamesLaterToday = []
|
elif game["startdate"].day == datetime.now().day:
|
||||||
gamesToday = []
|
gamesToday.append(game)
|
||||||
gamesUpcoming = []
|
elif game["startdate"].day > datetime.now().day:
|
||||||
gamesEarlierWeek = []
|
gamesUpcoming.append(game)
|
||||||
|
|
||||||
# sort games
|
|
||||||
for game in games["games"]:
|
|
||||||
if game["time"] is not None:
|
|
||||||
liveGames.append(game)
|
|
||||||
elif game["quarter"] == "P" and game["startdate"].day == datetime.now().day:
|
|
||||||
gamesLaterToday.append(game)
|
|
||||||
elif game["startdate"].day == datetime.now().day:
|
|
||||||
gamesToday.append(game)
|
|
||||||
elif game["startdate"].day > datetime.now().day:
|
|
||||||
gamesUpcoming.append(game)
|
|
||||||
else:
|
|
||||||
gamesEarlierWeek.append(game)
|
|
||||||
|
|
||||||
# create list of formatted games
|
|
||||||
liveGamesStr = []
|
|
||||||
for game in liveGames:
|
|
||||||
liveGamesStr.append(self.formatGameLive(game))
|
|
||||||
liveGamesStr = ", ".join(liveGamesStr)
|
|
||||||
|
|
||||||
gamesLaterTodayStr = []
|
|
||||||
for game in gamesLaterToday:
|
|
||||||
gamesLaterTodayStr.append(self.formatGameFuture(game))
|
|
||||||
gamesLaterTodayStr = ", ".join(gamesLaterTodayStr)
|
|
||||||
|
|
||||||
gamesTodayStr = []
|
|
||||||
for game in gamesToday:
|
|
||||||
gamesTodayStr.append(self.formatGamePast(game))
|
|
||||||
gamesTodayStr = ", ".join(gamesTodayStr)
|
|
||||||
|
|
||||||
gamesUpcomingStr = []
|
|
||||||
for game in gamesUpcoming:
|
|
||||||
gamesUpcomingStr.append(self.formatGameFuture(game))
|
|
||||||
gamesUpcomingStr = ", ".join(gamesUpcomingStr)
|
|
||||||
|
|
||||||
gamesEarlierWeekStr = []
|
|
||||||
for game in gamesEarlierWeek:
|
|
||||||
gamesEarlierWeekStr.append(self.formatGamePast(game))
|
|
||||||
gamesEarlierWeekStr = ", ".join(gamesEarlierWeekStr)
|
|
||||||
|
|
||||||
msgPieces = []
|
|
||||||
|
|
||||||
msgPieces.append("\x02NFL week %s\x02:" % (games["season"]["week"]))
|
|
||||||
|
|
||||||
# Depending on args build the respon pieces
|
|
||||||
if len(cmd.args) > 0 and cmd.args[0] == "today":
|
|
||||||
if not liveGamesStr == "":
|
|
||||||
msgPieces.append("\x02Playing now:\x02 %s" % liveGamesStr)
|
|
||||||
if not gamesLaterTodayStr == "":
|
|
||||||
msgPieces.append("\x02Later today:\x02 %s" % gamesLaterTodayStr)
|
|
||||||
if not gamesTodayStr == "":
|
|
||||||
msgPieces.append("\x02Earlier today:\x02 %s" % gamesTodayStr)
|
|
||||||
elif len(cmd.args) > 0 and cmd.args[0] == "live":
|
|
||||||
if not liveGamesStr == "":
|
|
||||||
msgPieces.append("\x02Playing now:\x02 %s" % liveGamesStr)
|
|
||||||
elif len(cmd.args) > 0 and cmd.args[0] == "scores":
|
|
||||||
if not liveGamesStr == "":
|
|
||||||
msgPieces.append("\x02Playing now:\x02 %s" % liveGamesStr)
|
|
||||||
if not gamesTodayStr == "":
|
|
||||||
msgPieces.append("\x02Earlier today:\x02 %s" % gamesTodayStr)
|
|
||||||
if not gamesEarlierWeekStr == "":
|
|
||||||
msgPieces.append("\x02Earlier this week: \x02 %s" % gamesEarlierWeekStr)
|
|
||||||
else:
|
else:
|
||||||
if not liveGamesStr == "":
|
gamesEarlierWeek.append(game)
|
||||||
msgPieces.append("\x02Playing now:\x02 %s" % liveGamesStr)
|
|
||||||
if not gamesLaterTodayStr == "":
|
|
||||||
msgPieces.append("\x02Later today:\x02 %s" % gamesLaterTodayStr)
|
|
||||||
if not gamesTodayStr == "":
|
|
||||||
msgPieces.append("\x02Earlier today:\x02 %s" % gamesTodayStr)
|
|
||||||
if not gamesEarlierWeekStr == "":
|
|
||||||
msgPieces.append("\x02Earlier this week: \x02 %s" % gamesEarlierWeekStr)
|
|
||||||
if not gamesUpcomingStr == "":
|
|
||||||
msgPieces.append("\x02Upcoming:\x02 %s" % gamesUpcomingStr)
|
|
||||||
|
|
||||||
# Collaspe the list into a repsonse string. Fix grammar
|
# create list of formatted games
|
||||||
msg = ", ".join(msgPieces).replace(":, ", ": ")
|
liveGamesStr = []
|
||||||
|
for game in liveGames:
|
||||||
|
liveGamesStr.append(self.formatGameLive(game))
|
||||||
|
liveGamesStr = ", ".join(liveGamesStr)
|
||||||
|
|
||||||
# Nothing means there were probably no games
|
gamesLaterTodayStr = []
|
||||||
if len(msgPieces) == 1:
|
for game in gamesLaterToday:
|
||||||
msg = "No games!"
|
gamesLaterTodayStr.append(self.formatGameFuture(game))
|
||||||
|
gamesLaterTodayStr = ", ".join(gamesLaterTodayStr)
|
||||||
|
|
||||||
if len(msg) > 0:
|
gamesTodayStr = []
|
||||||
# The message can be long so chunk it into pieces splitting at commas
|
for game in gamesToday:
|
||||||
while len(msg) > 0:
|
gamesTodayStr.append(self.formatGamePast(game))
|
||||||
piece = msg[0:330]
|
gamesTodayStr = ", ".join(gamesTodayStr)
|
||||||
msg = msg[330:]
|
|
||||||
while not piece[-1:] == "," and len(msg) > 0:
|
gamesUpcomingStr = []
|
||||||
piece += msg[0:1]
|
for game in gamesUpcoming:
|
||||||
msg = msg[1:]
|
gamesUpcomingStr.append(self.formatGameFuture(game))
|
||||||
self.bot.act_PRIVMSG(replyTo, "%s: %s" % (prefix.nick, piece.strip()))
|
gamesUpcomingStr = ", ".join(gamesUpcomingStr)
|
||||||
|
|
||||||
|
gamesEarlierWeekStr = []
|
||||||
|
for game in gamesEarlierWeek:
|
||||||
|
gamesEarlierWeekStr.append(self.formatGamePast(game))
|
||||||
|
gamesEarlierWeekStr = ", ".join(gamesEarlierWeekStr)
|
||||||
|
|
||||||
|
msgPieces = []
|
||||||
|
|
||||||
|
msgPieces.append("\x02NFL week %s\x02:" % (games["season"]["week"]))
|
||||||
|
|
||||||
|
# Depending on args build the respon pieces
|
||||||
|
if len(cmd.args) > 0 and cmd.args[0] == "today":
|
||||||
|
if not liveGamesStr == "":
|
||||||
|
msgPieces.append("\x02Playing now:\x02 %s" % liveGamesStr)
|
||||||
|
if not gamesLaterTodayStr == "":
|
||||||
|
msgPieces.append("\x02Later today:\x02 %s" % gamesLaterTodayStr)
|
||||||
|
if not gamesTodayStr == "":
|
||||||
|
msgPieces.append("\x02Earlier today:\x02 %s" % gamesTodayStr)
|
||||||
|
elif len(cmd.args) > 0 and cmd.args[0] == "live":
|
||||||
|
if not liveGamesStr == "":
|
||||||
|
msgPieces.append("\x02Playing now:\x02 %s" % liveGamesStr)
|
||||||
|
elif len(cmd.args) > 0 and cmd.args[0] == "scores":
|
||||||
|
if not liveGamesStr == "":
|
||||||
|
msgPieces.append("\x02Playing now:\x02 %s" % liveGamesStr)
|
||||||
|
if not gamesTodayStr == "":
|
||||||
|
msgPieces.append("\x02Earlier today:\x02 %s" % gamesTodayStr)
|
||||||
|
if not gamesEarlierWeekStr == "":
|
||||||
|
msgPieces.append("\x02Earlier this week: \x02 %s" % gamesEarlierWeekStr)
|
||||||
|
else:
|
||||||
|
if not liveGamesStr == "":
|
||||||
|
msgPieces.append("\x02Playing now:\x02 %s" % liveGamesStr)
|
||||||
|
if not gamesLaterTodayStr == "":
|
||||||
|
msgPieces.append("\x02Later today:\x02 %s" % gamesLaterTodayStr)
|
||||||
|
if not gamesTodayStr == "":
|
||||||
|
msgPieces.append("\x02Earlier today:\x02 %s" % gamesTodayStr)
|
||||||
|
if not gamesEarlierWeekStr == "":
|
||||||
|
msgPieces.append("\x02Earlier this week: \x02 %s" % gamesEarlierWeekStr)
|
||||||
|
if not gamesUpcomingStr == "":
|
||||||
|
msgPieces.append("\x02Upcoming:\x02 %s" % gamesUpcomingStr)
|
||||||
|
|
||||||
|
# Collaspe the list into a repsonse string. Fix grammar
|
||||||
|
msg = ", ".join(msgPieces).replace(":, ", ": ")
|
||||||
|
|
||||||
|
# Nothing means there were probably no games
|
||||||
|
if len(msgPieces) == 1:
|
||||||
|
msg = "No games!"
|
||||||
|
|
||||||
|
if len(msg) > 0:
|
||||||
|
# The message can be long so chunk it into pieces splitting at commas
|
||||||
|
while len(msg) > 0:
|
||||||
|
piece = msg[0:330]
|
||||||
|
msg = msg[330:]
|
||||||
|
while not piece[-1:] == "," and len(msg) > 0:
|
||||||
|
piece += msg[0:1]
|
||||||
|
msg = msg[1:]
|
||||||
|
self.bot.act_PRIVMSG(message.args[0], "%s: %s" % (message.prefix.nick, piece.strip()))
|
||||||
|
|
||||||
def formatGameLive(self, game):
|
def formatGameLive(self, game):
|
||||||
c_vis = 3 if int(game["visitor_score"]) > int(game["home_score"]) else 4
|
c_vis = 3 if int(game["visitor_score"]) > int(game["home_score"]) else 4
|
||||||
|
|
|
@ -7,14 +7,14 @@
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from pyircbot.modulebase import ModuleBase, ModuleHook
|
from pyircbot.modulebase import ModuleBase, hook
|
||||||
import hashlib
|
from pyircbot.common import messageHasCommand
|
||||||
|
from pyircbot.modules.ModInfo import info
|
||||||
|
|
||||||
|
|
||||||
class NickUser(ModuleBase):
|
class NickUser(ModuleBase):
|
||||||
def __init__(self, bot, moduleName):
|
def __init__(self, bot, moduleName):
|
||||||
ModuleBase.__init__(self, bot, moduleName)
|
ModuleBase.__init__(self, bot, moduleName)
|
||||||
self.hooks = [ModuleHook("PRIVMSG", self.gotmsg)]
|
|
||||||
self.services = ["login"]
|
self.services = ["login"]
|
||||||
|
|
||||||
def check(self, nick, hostname):
|
def check(self, nick, hostname):
|
||||||
|
@ -28,17 +28,19 @@ class NickUser(ModuleBase):
|
||||||
pass
|
pass
|
||||||
# TODO: log out all users
|
# TODO: log out all users
|
||||||
|
|
||||||
def gotmsg(self, args, prefix, trailing):
|
@hook("PRIVMSG")
|
||||||
channel = args[0]
|
def gotmsg(self, msg, cmd):
|
||||||
if channel[0] == "#":
|
if msg.args[0][0] == "#":
|
||||||
# Ignore channel messages
|
# Ignore channel messages
|
||||||
pass
|
return
|
||||||
else:
|
else:
|
||||||
self.handlePm(args, prefix, trailing)
|
self.handlePm(msg.prefix, msg.trailing)
|
||||||
|
|
||||||
def handlePm(self, args, prefix, trailing):
|
@info("setpass [<oldpass>] <password> set or change password", cmds=["setpass"])
|
||||||
prefix = self.bot.decodePrefix(prefix)
|
@info("login <password> authenticate with the bot", cmds=["login"])
|
||||||
cmd = self.bot.messageHasCommand(".setpass", trailing)
|
@info("logout log out of the bot", cmds=["logout"])
|
||||||
|
def handlePm(self, prefix, trailing):
|
||||||
|
cmd = messageHasCommand(".setpass", trailing)
|
||||||
if cmd:
|
if cmd:
|
||||||
if len(cmd.args) == 0:
|
if len(cmd.args) == 0:
|
||||||
self.bot.act_PRIVMSG(prefix.nick, ".setpass: usage: \".setpass newpass\" or "
|
self.bot.act_PRIVMSG(prefix.nick, ".setpass: usage: \".setpass newpass\" or "
|
||||||
|
@ -61,7 +63,7 @@ class NickUser(ModuleBase):
|
||||||
self.bot.act_PRIVMSG(prefix.nick,
|
self.bot.act_PRIVMSG(prefix.nick,
|
||||||
".setpass: You must provide the old password when setting a new one.")
|
".setpass: You must provide the old password when setting a new one.")
|
||||||
|
|
||||||
cmd = self.bot.messageHasCommand(".login", trailing)
|
cmd = messageHasCommand(".login", trailing)
|
||||||
if cmd:
|
if cmd:
|
||||||
attr = self.bot.getBestModuleForService("attributes")
|
attr = self.bot.getBestModuleForService("attributes")
|
||||||
userpw = attr.getKey(prefix.nick, "password")
|
userpw = attr.getKey(prefix.nick, "password")
|
||||||
|
@ -78,7 +80,7 @@ class NickUser(ModuleBase):
|
||||||
self.bot.act_PRIVMSG(prefix.nick, ".login: incorrect password.")
|
self.bot.act_PRIVMSG(prefix.nick, ".login: incorrect password.")
|
||||||
else:
|
else:
|
||||||
self.bot.act_PRIVMSG(prefix.nick, ".login: usage: \".login password\"")
|
self.bot.act_PRIVMSG(prefix.nick, ".login: usage: \".login password\"")
|
||||||
cmd = self.bot.messageHasCommand(".logout", trailing)
|
cmd = messageHasCommand(".logout", trailing)
|
||||||
if cmd:
|
if cmd:
|
||||||
attr = self.bot.getBestModuleForService("attributes")
|
attr = self.bot.getBestModuleForService("attributes")
|
||||||
loggedin = attr.getKey(prefix.nick, "loggedinfrom")
|
loggedin = attr.getKey(prefix.nick, "loggedinfrom")
|
||||||
|
@ -87,8 +89,3 @@ class NickUser(ModuleBase):
|
||||||
else:
|
else:
|
||||||
attr.setKey(prefix.nick, "loggedinfrom", None)
|
attr.setKey(prefix.nick, "loggedinfrom", None)
|
||||||
self.bot.act_PRIVMSG(prefix.nick, ".logout: You have been logged out.")
|
self.bot.act_PRIVMSG(prefix.nick, ".logout: You have been logged out.")
|
||||||
|
|
||||||
def md5(self, data):
|
|
||||||
m = hashlib.md5()
|
|
||||||
m.update(data.encode("ascii"))
|
|
||||||
return m.hexdigest()
|
|
||||||
|
|
|
@ -9,26 +9,23 @@
|
||||||
|
|
||||||
from time import time, sleep
|
from time import time, sleep
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
from pyircbot.modulebase import ModuleBase, ModuleHook
|
from pyircbot.modulebase import ModuleBase, hook
|
||||||
|
|
||||||
|
|
||||||
class PingResponder(ModuleBase):
|
class PingResponder(ModuleBase):
|
||||||
def __init__(self, bot, moduleName):
|
def __init__(self, bot, moduleName):
|
||||||
ModuleBase.__init__(self, bot, moduleName)
|
ModuleBase.__init__(self, bot, moduleName)
|
||||||
self.timer = PingRespondTimer(self)
|
self.timer = PingRespondTimer(self)
|
||||||
self.hooks = [
|
|
||||||
ModuleHook("PING", self.pingrespond),
|
|
||||||
ModuleHook("_RECV", self.resettimer),
|
|
||||||
ModuleHook("_SEND", self.resettimer)
|
|
||||||
]
|
|
||||||
|
|
||||||
def pingrespond(self, args, prefix, trailing):
|
@hook("PING")
|
||||||
|
def pingrespond(self, msg, cmd):
|
||||||
"""Respond to the PING command"""
|
"""Respond to the PING command"""
|
||||||
# got a ping? send it right back
|
# got a ping? send it right back
|
||||||
self.bot.act_PONG(trailing)
|
self.bot.act_PONG(msg.trailing)
|
||||||
self.log.info("%s Responded to a ping: %s" % (self.bot.get_nick(), trailing))
|
self.log.info("%s Responded to a ping: %s" % (self.bot.get_nick(), msg.trailing))
|
||||||
|
|
||||||
def resettimer(self, msg):
|
@hook("_RECV", "_SEND")
|
||||||
|
def resettimer(self, msg, cmd):
|
||||||
"""Resets the connection failure timer"""
|
"""Resets the connection failure timer"""
|
||||||
self.timer.reset()
|
self.timer.reset()
|
||||||
|
|
||||||
|
@ -37,7 +34,9 @@ class PingResponder(ModuleBase):
|
||||||
|
|
||||||
|
|
||||||
class PingRespondTimer(Thread):
|
class PingRespondTimer(Thread):
|
||||||
"Tracks last ping from server, and reconnects if over a threshold"
|
"""
|
||||||
|
Tracks last ping from server, and reconnects if over a threshold
|
||||||
|
"""
|
||||||
def __init__(self, master):
|
def __init__(self, master):
|
||||||
Thread.__init__(self)
|
Thread.__init__(self)
|
||||||
self.daemon = True
|
self.daemon = True
|
||||||
|
@ -47,11 +46,15 @@ class PingRespondTimer(Thread):
|
||||||
self.start()
|
self.start()
|
||||||
|
|
||||||
def reset(self):
|
def reset(self):
|
||||||
"Reset the internal ping timeout counter"
|
"""
|
||||||
|
Reset the internal ping timeout counter
|
||||||
|
"""
|
||||||
self.lastping = time()
|
self.lastping = time()
|
||||||
|
|
||||||
def disable(self):
|
def disable(self):
|
||||||
"Allow the thread to die"
|
"""
|
||||||
|
Allow the thread to die
|
||||||
|
"""
|
||||||
self.alive = False
|
self.alive = False
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
|
|
|
@ -62,28 +62,28 @@ class PubSubClient(ModuleBase):
|
||||||
self.bus.pub(self.config.get("publish").format(subchannel), "{} {}".format("default", message))
|
self.bus.pub(self.config.get("publish").format(subchannel), "{} {}".format("default", message))
|
||||||
|
|
||||||
@hook("PRIVMSG")
|
@hook("PRIVMSG")
|
||||||
def bus_privmsg(self, msg):
|
def bus_privmsg(self, msg, cmd):
|
||||||
"""
|
"""
|
||||||
Relay a privmsg to the event bus
|
Relay a privmsg to the event bus
|
||||||
"""
|
"""
|
||||||
self.publish("privmsg", dumps([msg.args, msg.prefix[0], msg.trailing, {"prefix": msg.prefix}]))
|
self.publish("privmsg", dumps([msg.args, msg.prefix[0], msg.trailing, {"prefix": msg.prefix}]))
|
||||||
|
|
||||||
@hook("JOIN")
|
@hook("JOIN")
|
||||||
def bus_join(self, msg):
|
def bus_join(self, msg, cmd):
|
||||||
"""
|
"""
|
||||||
Relay a join message to the event bus
|
Relay a join message to the event bus
|
||||||
"""
|
"""
|
||||||
self.publish("join", dumps([msg.prefix[0], msg.trailing, {"prefix": msg.prefix}]))
|
self.publish("join", dumps([msg.prefix[0], msg.trailing, {"prefix": msg.prefix}]))
|
||||||
|
|
||||||
@hook("PART")
|
@hook("PART")
|
||||||
def bus_part(self, msg):
|
def bus_part(self, msg, cmd):
|
||||||
"""
|
"""
|
||||||
Relay a part message to the event bus
|
Relay a part message to the event bus
|
||||||
"""
|
"""
|
||||||
self.publish("part", dumps([msg.args, msg.prefix[0], msg.trailing, {"prefix": msg.prefix}]))
|
self.publish("part", dumps([msg.args, msg.prefix[0], msg.trailing, {"prefix": msg.prefix}]))
|
||||||
|
|
||||||
@hook("PRIVMSG")
|
@hook("PRIVMSG")
|
||||||
def bus_command(self, msg):
|
def bus_command(self, msg, cmd):
|
||||||
"""
|
"""
|
||||||
Parse commands and publish as separate channels on the bus. Commands like `.seen nick` will be published
|
Parse commands and publish as separate channels on the bus. Commands like `.seen nick` will be published
|
||||||
to channel `command_seen`.
|
to channel `command_seen`.
|
||||||
|
|
|
@ -7,14 +7,14 @@
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from pyircbot.modulebase import ModuleBase, ModuleHook
|
from pyircbot.modulebase import ModuleBase, hook, command
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
from pyircbot.modules.ModInfo import info
|
||||||
|
|
||||||
|
|
||||||
class RandQuote(ModuleBase):
|
class RandQuote(ModuleBase):
|
||||||
def __init__(self, bot, moduleName):
|
def __init__(self, bot, moduleName):
|
||||||
ModuleBase.__init__(self, bot, moduleName)
|
ModuleBase.__init__(self, bot, moduleName)
|
||||||
self.hooks = []
|
|
||||||
self.db = None
|
self.db = None
|
||||||
serviceProviders = self.bot.getmodulesbyservice("sqlite")
|
serviceProviders = self.bot.getmodulesbyservice("sqlite")
|
||||||
if not serviceProviders:
|
if not serviceProviders:
|
||||||
|
@ -33,26 +33,19 @@ class RandQuote(ModuleBase):
|
||||||
) ;""")
|
) ;""")
|
||||||
c.close()
|
c.close()
|
||||||
|
|
||||||
self.hooks = [ModuleHook("PRIVMSG", self.logquote),
|
@info("randquote print a random quote", cmds=["randquote", "randomquote", "rq"])
|
||||||
ModuleHook("PRIVMSG", self.fetchquotes)]
|
@command("randquote", "randomquote", "rq")
|
||||||
|
def fetchquotes(self, msg, cmd):
|
||||||
|
c = self.db.query("SELECT * FROM `chat` ORDER BY RANDOM() LIMIT 1;")
|
||||||
|
row = c.fetchone()
|
||||||
|
c.close()
|
||||||
|
if row:
|
||||||
|
self.bot.act_PRIVMSG(msg.args[0], "<%s> %s" % (row["sender"], row["message"],))
|
||||||
|
|
||||||
def fetchquotes(self, args, prefix, trailing):
|
@hook("PRIVMSG")
|
||||||
if not args[0][0] == "#":
|
def logquote(self, msg, cmd):
|
||||||
return
|
|
||||||
cmd = self.bot.messageHasCommand([".randomquote", ".randquote", ".rq"], trailing)
|
|
||||||
if cmd:
|
|
||||||
c = self.db.query("SELECT * FROM `chat` ORDER BY RANDOM() LIMIT 1;")
|
|
||||||
row = c.fetchone()
|
|
||||||
c.close()
|
|
||||||
if row:
|
|
||||||
self.bot.act_PRIVMSG(args[0], "<%s> %s" % (row["sender"], row["message"],))
|
|
||||||
|
|
||||||
def logquote(self, args, prefix, trailing):
|
|
||||||
if not args[0][0] == "#":
|
|
||||||
return
|
|
||||||
prefixObj = self.bot.decodePrefix(prefix)
|
|
||||||
self.db.query("INSERT INTO `chat` (`date`, `sender`, `message`) VALUES (?, ?, ?)",
|
self.db.query("INSERT INTO `chat` (`date`, `sender`, `message`) VALUES (?, ?, ?)",
|
||||||
(int(datetime.now().timestamp()), prefixObj.nick, trailing)).close()
|
(int(datetime.now().timestamp()), msg.prefix.nick, msg.trailing)).close()
|
||||||
# Trim quotes
|
# Trim quotes
|
||||||
c = self.db.query("SELECT * FROM `chat` ORDER BY `date` DESC LIMIT %s, 1000000;" % self.config["limit"])
|
c = self.db.query("SELECT * FROM `chat` ORDER BY `date` DESC LIMIT %s, 1000000;" % self.config["limit"])
|
||||||
while True:
|
while True:
|
||||||
|
|
|
@ -6,12 +6,13 @@
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from pyircbot.modulebase import ModuleBase, ModuleHook
|
from pyircbot.modulebase import ModuleBase, command
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
from time import sleep
|
from time import sleep
|
||||||
import re
|
import re
|
||||||
import pytz
|
import pytz
|
||||||
|
from pyircbot.modules.ModInfo import info
|
||||||
|
|
||||||
|
|
||||||
class Remind(ModuleBase):
|
class Remind(ModuleBase):
|
||||||
|
@ -37,9 +38,6 @@ class Remind(ModuleBase):
|
||||||
) ;""")
|
) ;""")
|
||||||
c.close()
|
c.close()
|
||||||
|
|
||||||
self.hooks = [ModuleHook("PRIVMSG", self.remindin),
|
|
||||||
ModuleHook("PRIVMSG", self.remindat)]
|
|
||||||
|
|
||||||
self.disabled = False
|
self.disabled = False
|
||||||
|
|
||||||
# Start monitor thread
|
# Start monitor thread
|
||||||
|
@ -108,86 +106,81 @@ class Remind(ModuleBase):
|
||||||
def ondisable(self):
|
def ondisable(self):
|
||||||
self.disabled = True
|
self.disabled = True
|
||||||
|
|
||||||
def remindat(self, args, prefix, trailing):
|
@info("remind <time> have the bot remind you", cmds=["remind", "at"])
|
||||||
prefixObj = self.bot.decodePrefix(prefix)
|
@command("remind", "at", allow_private=True)
|
||||||
|
def remindat(self, msg, cmd):
|
||||||
|
regex = re.compile(r'(\d+):(\d+)(?::(\d+))?([^\s\d]+)? (.*)')
|
||||||
|
match = regex.match(cmd.args_str)
|
||||||
|
replyTo = msg.args[0]
|
||||||
|
try:
|
||||||
|
hour, minute, second, tz, message = match.groups()
|
||||||
|
message = message.strip()
|
||||||
|
|
||||||
replyTo = prefixObj.nick if "#" not in args[0] else args[0]
|
assert not message == ""
|
||||||
|
|
||||||
# Lots of code borrowed from https://github.com/embolalia/willie/blob/master/willie/modules/remind.py
|
hour = int(hour)
|
||||||
cmd = self.bot.messageHasCommand([".at", ".remind"], trailing, True)
|
minute = int(minute)
|
||||||
if cmd:
|
if second is not None:
|
||||||
regex = re.compile(r'(\d+):(\d+)(?::(\d+))?([^\s\d]+)? (.*)')
|
second = int(second)
|
||||||
match = regex.match(cmd.args_str)
|
|
||||||
|
|
||||||
|
except:
|
||||||
|
self.bot.act_PRIVMSG(replyTo, "%s: .at - Remind at a time. Example: .at 20:45EST Do your homework!" %
|
||||||
|
msg.prefix.nick)
|
||||||
|
return
|
||||||
|
|
||||||
|
now = datetime.now()
|
||||||
|
remindAt = datetime.now()
|
||||||
|
|
||||||
|
# if there was timezone, make sure the time we're reminding them at is relative to their timezone
|
||||||
|
if tz is not None:
|
||||||
try:
|
try:
|
||||||
hour, minute, second, tz, message = match.groups()
|
theirzone = pytz.timezone(Remind.translateZoneStr(tz))
|
||||||
message = message.strip()
|
|
||||||
|
|
||||||
assert not message == ""
|
|
||||||
|
|
||||||
hour = int(hour)
|
|
||||||
minute = int(minute)
|
|
||||||
if second is not None:
|
|
||||||
second = int(second)
|
|
||||||
|
|
||||||
except:
|
except:
|
||||||
self.bot.act_PRIVMSG(replyTo, "%s: .at - Remind at a time. Example: .at 20:45EST Do your homework!" %
|
self.bot.act_PRIVMSG(replyTo, "%s: I don't recognize the timezone '%s'." % (msg.prefix.nick, tz))
|
||||||
prefixObj.nick)
|
|
||||||
return
|
return
|
||||||
|
remindAt = theirzone.localize(remindAt, is_dst=Remind.is_dst(theirzone))
|
||||||
|
|
||||||
now = datetime.now()
|
# Set the hour and minute we'll remind them at today.
|
||||||
remindAt = datetime.now()
|
# If the ends up being in the past, we'll add a day alter
|
||||||
|
remindAt = remindAt.replace(hour=hour).replace(minute=minute).replace(microsecond=0)
|
||||||
|
|
||||||
# if there was timezone, make sure the time we're reminding them at is relative to their timezone
|
# Set seconds
|
||||||
if tz is not None:
|
if second is None:
|
||||||
try:
|
remindAt = remindAt.replace(second=0)
|
||||||
theirzone = pytz.timezone(Remind.translateZoneStr(tz))
|
else:
|
||||||
except:
|
remindAt = remindAt.replace(second=second)
|
||||||
self.bot.act_PRIVMSG(replyTo, "%s: I don't recognize the timezone '%s'." % (prefixObj.nick, tz))
|
|
||||||
return
|
|
||||||
remindAt = theirzone.localize(remindAt, is_dst=Remind.is_dst(theirzone))
|
|
||||||
|
|
||||||
# Set the hour and minute we'll remind them at today.
|
# if there was timezone, convert remindAt to our zone
|
||||||
# If the ends up being in the past, we'll add a day alter
|
if tz is not None:
|
||||||
remindAt = remindAt.replace(hour=hour).replace(minute=minute).replace(microsecond=0)
|
try:
|
||||||
|
theirzone = pytz.timezone(Remind.translateZoneStr(tz))
|
||||||
|
except:
|
||||||
|
self.bot.act_PRIVMSG(replyTo, "%s: I don't recognize the timezone '%s'." % (msg.prefix.nick, tz))
|
||||||
|
return
|
||||||
|
remindAt = remindAt.astimezone(pytz.timezone(self.config["mytimezone"])).replace(tzinfo=None)
|
||||||
|
|
||||||
# Set seconds
|
# Advance it a day if the time would have been earlier today
|
||||||
if second is None:
|
while remindAt < now:
|
||||||
remindAt = remindAt.replace(second=0)
|
remindAt += timedelta(days=1)
|
||||||
else:
|
|
||||||
remindAt = remindAt.replace(second=second)
|
|
||||||
|
|
||||||
# if there was timezone, convert remindAt to our zone
|
timediff = remindAt - datetime.now()
|
||||||
if tz is not None:
|
# self.bot.act_PRIVMSG(replyTo, "Time: %s" % str(remindAt))
|
||||||
try:
|
# self.bot.act_PRIVMSG(replyTo, "Diff: %s" % (timediff))
|
||||||
theirzone = pytz.timezone(Remind.translateZoneStr(tz))
|
|
||||||
except:
|
|
||||||
self.bot.act_PRIVMSG(replyTo, "%s: I don't recognize the timezone '%s'." % (prefixObj.nick, tz))
|
|
||||||
return
|
|
||||||
remindAt = remindAt.astimezone(pytz.timezone(self.config["mytimezone"])).replace(tzinfo=None)
|
|
||||||
|
|
||||||
# Advance it a day if the time would have been earlier today
|
# Save the reminder
|
||||||
while remindAt < now:
|
c = self.db.query("INSERT INTO `reminders` (`sender`, `senderch`, `when`, `message`) VALUES (?, ?, ?, ?)", (
|
||||||
remindAt += timedelta(days=1)
|
msg.prefix.nick,
|
||||||
|
msg.args[0] if "#" in msg.args[0] else "",
|
||||||
|
remindAt,
|
||||||
|
message
|
||||||
|
))
|
||||||
|
c.close()
|
||||||
|
|
||||||
timediff = remindAt - datetime.now()
|
diffHours = int(timediff.seconds / 60 / 60)
|
||||||
# self.bot.act_PRIVMSG(replyTo, "Time: %s" % str(remindAt))
|
diffMins = int((timediff.seconds - diffHours * 60 * 60) / 60)
|
||||||
# self.bot.act_PRIVMSG(replyTo, "Diff: %s" % (timediff))
|
|
||||||
|
|
||||||
# Save the reminder
|
self.bot.act_PRIVMSG(replyTo, "%s: Ok, will do. Approx %sh%sm to go." %
|
||||||
c = self.db.query("INSERT INTO `reminders` (`sender`, `senderch`, `when`, `message`) VALUES (?, ?, ?, ?)", (
|
(msg.prefix.nick, diffHours, diffMins))
|
||||||
prefixObj.nick,
|
|
||||||
args[0] if "#" in args[0] else "",
|
|
||||||
remindAt,
|
|
||||||
message
|
|
||||||
))
|
|
||||||
c.close()
|
|
||||||
|
|
||||||
diffHours = int(timediff.seconds / 60 / 60)
|
|
||||||
diffMins = int((timediff.seconds - diffHours * 60 * 60) / 60)
|
|
||||||
|
|
||||||
self.bot.act_PRIVMSG(replyTo, "%s: Ok, will do. Approx %sh%sm to go." %
|
|
||||||
(prefixObj.nick, diffHours, diffMins))
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def is_dst(tz):
|
def is_dst(tz):
|
||||||
|
@ -205,46 +198,45 @@ class Remind(ModuleBase):
|
||||||
else:
|
else:
|
||||||
return zonestr
|
return zonestr
|
||||||
|
|
||||||
def remindin(self, args, prefix, trailing):
|
@info("after <duration> have the bot remind after", cmds=["after", "in"])
|
||||||
prefixObj = self.bot.decodePrefix(prefix)
|
@command("after", "in", allow_private=True)
|
||||||
replyTo = prefixObj.nick if "#" not in args[0] else args[0]
|
def remindin(self, msg, cmd):
|
||||||
|
replyTo = msg.args[0]
|
||||||
|
|
||||||
cmd = self.bot.messageHasCommand([".in", ".after"], trailing, True)
|
if not cmd.args:
|
||||||
if cmd:
|
self.bot.act_PRIVMSG(replyTo, "%s: .in - Remind after x amount of time. Example: .in 1week5d2h1m Go "
|
||||||
if not cmd.args:
|
"fuck yourself" % msg.prefix.nick)
|
||||||
self.bot.act_PRIVMSG(replyTo, "%s: .in - Remind after x amount of time. Example: .in 1week5d2h1m Go "
|
return
|
||||||
"fuck yourself" % prefixObj.nick)
|
|
||||||
|
timepieces = re.compile(r'([0-9]+)([a-zA-Z]+)').findall(cmd.args[0])
|
||||||
|
if not timepieces:
|
||||||
|
self.bot.act_PRIVMSG(replyTo, "%s: .in - Remind after x amount of time. Example: .in 1week5d2h1m Go "
|
||||||
|
"fuck yourself" % msg.prefix.nick)
|
||||||
|
return
|
||||||
|
|
||||||
|
delaySeconds = 0
|
||||||
|
for match in timepieces:
|
||||||
|
# ('30', 'm')
|
||||||
|
if not match[1] in Remind.scaling:
|
||||||
|
self.bot.act_PRIVMSG(replyTo, "%s: Sorry, I don't understand the time unit '%s'" %
|
||||||
|
(msg.prefix.nick, match[1]))
|
||||||
return
|
return
|
||||||
|
|
||||||
timepieces = re.compile(r'([0-9]+)([a-zA-Z]+)').findall(cmd.args[0])
|
delaySeconds += (Remind.scaling[match[1]] * int(match[0]))
|
||||||
if not timepieces:
|
|
||||||
self.bot.act_PRIVMSG(replyTo, "%s: .in - Remind after x amount of time. Example: .in 1week5d2h1m Go "
|
|
||||||
"fuck yourself" % prefixObj.nick)
|
|
||||||
return
|
|
||||||
|
|
||||||
delaySeconds = 0
|
remindAt = datetime.now() + timedelta(seconds=delaySeconds)
|
||||||
for match in timepieces:
|
|
||||||
# ('30', 'm')
|
|
||||||
if not match[1] in Remind.scaling:
|
|
||||||
self.bot.act_PRIVMSG(replyTo, "%s: Sorry, I don't understand the time unit '%s'" %
|
|
||||||
(prefixObj.nick, match[1]))
|
|
||||||
return
|
|
||||||
|
|
||||||
delaySeconds += (Remind.scaling[match[1]] * int(match[0]))
|
self.db.query("INSERT INTO `reminders` (`sender`, `senderch`, `when`, `message`) VALUES (?, ?, ?, ?)", (
|
||||||
|
msg.prefix.nick,
|
||||||
|
msg.args[0] if "#" in msg.args[0] else "",
|
||||||
|
remindAt,
|
||||||
|
cmd.args_str[len(cmd.args[0]):].strip()
|
||||||
|
)).close()
|
||||||
|
|
||||||
remindAt = datetime.now() + timedelta(seconds=delaySeconds)
|
hours = int(delaySeconds / 60 / 60)
|
||||||
|
minutes = int((delaySeconds - (hours * 60 * 60)) / 60)
|
||||||
|
|
||||||
self.db.query("INSERT INTO `reminders` (`sender`, `senderch`, `when`, `message`) VALUES (?, ?, ?, ?)", (
|
self.bot.act_PRIVMSG(replyTo, "%s: Ok, talk to you in approx %sh%sm" % (msg.prefix.nick, hours, minutes))
|
||||||
prefixObj.nick,
|
|
||||||
args[0] if "#" in args[0] else "",
|
|
||||||
remindAt,
|
|
||||||
cmd.args_str[len(cmd.args[0]):].strip()
|
|
||||||
)).close()
|
|
||||||
|
|
||||||
hours = int(delaySeconds / 60 / 60)
|
|
||||||
minutes = int((delaySeconds - (hours * 60 * 60)) / 60)
|
|
||||||
|
|
||||||
self.bot.act_PRIVMSG(replyTo, "%s: Ok, talk to you in approx %sh%sm" % (prefixObj.nick, hours, minutes))
|
|
||||||
|
|
||||||
scaling = {
|
scaling = {
|
||||||
"years": 365.25 * 24 * 3600,
|
"years": 365.25 * 24 * 3600,
|
||||||
|
|
|
@ -156,7 +156,9 @@ class SMS(ModuleBase):
|
||||||
try:
|
try:
|
||||||
self.twilio.api.account.messages.create(to=self.config["contacts"][contact],
|
self.twilio.api.account.messages.create(to=self.config["contacts"][contact],
|
||||||
from_=self.config["number"],
|
from_=self.config["number"],
|
||||||
body=msg.trailing[7 + len(contact):].strip())
|
body="{} <{}>: {}".format(msg.args[0],
|
||||||
|
msg.prefix.nick,
|
||||||
|
msg.trailing[7 + len(contact):].strip()))
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.bot.act_PRIVMSG(msg.args[0], "Could not send message: {}".format(repr(e)))
|
self.bot.act_PRIVMSG(msg.args[0], "Could not send message: {}".format(repr(e)))
|
||||||
else:
|
else:
|
||||||
|
|
|
@ -14,7 +14,6 @@ import sqlite3
|
||||||
class SQLite(ModuleBase):
|
class SQLite(ModuleBase):
|
||||||
def __init__(self, bot, moduleName):
|
def __init__(self, bot, moduleName):
|
||||||
ModuleBase.__init__(self, bot, moduleName)
|
ModuleBase.__init__(self, bot, moduleName)
|
||||||
self.hooks = []
|
|
||||||
self.services = ["sqlite"]
|
self.services = ["sqlite"]
|
||||||
|
|
||||||
def opendb(self, dbname):
|
def opendb(self, dbname):
|
||||||
|
|
|
@ -7,15 +7,15 @@
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from pyircbot.modulebase import ModuleBase, ModuleHook
|
from pyircbot.modulebase import ModuleBase, hook
|
||||||
|
|
||||||
|
|
||||||
class ServerPassword(ModuleBase):
|
class ServerPassword(ModuleBase):
|
||||||
def __init__(self, bot, moduleName):
|
def __init__(self, bot, moduleName):
|
||||||
ModuleBase.__init__(self, bot, moduleName)
|
ModuleBase.__init__(self, bot, moduleName)
|
||||||
self.hooks = [ModuleHook("_CONNECT", self.doConnect)]
|
|
||||||
|
|
||||||
def doConnect(self, args, prefix, trailing):
|
@hook("_CONNECT")
|
||||||
|
def doConnect(self, msg, cmd):
|
||||||
"""Hook for when the IRC conneciton is opened"""
|
"""Hook for when the IRC conneciton is opened"""
|
||||||
if "password" in self.config and self.config["password"]:
|
if "password" in self.config and self.config["password"]:
|
||||||
self.log.info("Sending server password")
|
self.log.info("Sending server password")
|
||||||
|
|
|
@ -7,27 +7,25 @@
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from pyircbot.modulebase import ModuleBase, ModuleHook
|
from pyircbot.modulebase import ModuleBase, hook
|
||||||
from time import sleep
|
from time import sleep
|
||||||
|
|
||||||
|
|
||||||
class Services(ModuleBase):
|
class Services(ModuleBase):
|
||||||
def __init__(self, bot, moduleName):
|
def __init__(self, bot, moduleName):
|
||||||
ModuleBase.__init__(self, bot, moduleName)
|
ModuleBase.__init__(self, bot, moduleName)
|
||||||
self.hooks = [ModuleHook("_CONNECT", self.doConnect),
|
|
||||||
ModuleHook("433", self.nickTaken),
|
|
||||||
ModuleHook("001", self.initservices),
|
|
||||||
ModuleHook("INVITE", self.invited), ]
|
|
||||||
self.current_nick = 0
|
self.current_nick = 0
|
||||||
self.do_ghost = False
|
self.do_ghost = False
|
||||||
|
|
||||||
def doConnect(self, args, prefix, trailing):
|
@hook("_CONNECT")
|
||||||
|
def doConnect(self, msg, cmd):
|
||||||
"""Hook for when the IRC conneciton is opened"""
|
"""Hook for when the IRC conneciton is opened"""
|
||||||
self.bot.act_NICK(self.config["user"]["nick"][0])
|
self.bot.act_NICK(self.config["user"]["nick"][0])
|
||||||
self.bot.act_USER(self.config["user"]["username"], self.config["user"]["hostname"],
|
self.bot.act_USER(self.config["user"]["username"], self.config["user"]["hostname"],
|
||||||
self.config["user"]["realname"])
|
self.config["user"]["realname"])
|
||||||
|
|
||||||
def nickTaken(self, args, prefix, trailing):
|
@hook("433")
|
||||||
|
def nickTaken(self, msg, cmd):
|
||||||
"""Hook that responds to 433, meaning our nick is taken"""
|
"""Hook that responds to 433, meaning our nick is taken"""
|
||||||
if self.config["ident"]["ghost"]:
|
if self.config["ident"]["ghost"]:
|
||||||
self.do_ghost = True
|
self.do_ghost = True
|
||||||
|
@ -37,7 +35,8 @@ class Services(ModuleBase):
|
||||||
return
|
return
|
||||||
self.bot.act_NICK(self.config["user"]["nick"][self.current_nick])
|
self.bot.act_NICK(self.config["user"]["nick"][self.current_nick])
|
||||||
|
|
||||||
def initservices(self, args, prefix, trailing):
|
@hook("001")
|
||||||
|
def initservices(self, msg, cmd):
|
||||||
"""Hook that sets our initial nickname"""
|
"""Hook that sets our initial nickname"""
|
||||||
if self.do_ghost:
|
if self.do_ghost:
|
||||||
self.bot.act_PRIVMSG(self.config["ident"]["ghost_to"], self.config["ident"]["ghost_cmd"] %
|
self.bot.act_PRIVMSG(self.config["ident"]["ghost_to"], self.config["ident"]["ghost_cmd"] %
|
||||||
|
@ -46,11 +45,12 @@ class Services(ModuleBase):
|
||||||
self.bot.act_NICK(self.config["user"]["nick"][0])
|
self.bot.act_NICK(self.config["user"]["nick"][0])
|
||||||
self.do_initservices()
|
self.do_initservices()
|
||||||
|
|
||||||
def invited(self, args, prefix, trailing):
|
@hook("INVITE")
|
||||||
|
def invited(self, msg, cmd):
|
||||||
"""Hook responding to INVITE channel invitations"""
|
"""Hook responding to INVITE channel invitations"""
|
||||||
if trailing.lower() in self.config["privatechannels"]["list"]:
|
if msg.trailing.lower() in self.config["privatechannels"]["list"]:
|
||||||
self.log.info("Invited to %s, joining" % trailing)
|
self.log.info("Invited to %s, joining" % msg.trailing)
|
||||||
self.bot.act_JOIN(trailing)
|
self.bot.act_JOIN(msg.trailing)
|
||||||
|
|
||||||
def do_initservices(self):
|
def do_initservices(self):
|
||||||
"""Identify with nickserv and join startup channels"""
|
"""Identify with nickserv and join startup channels"""
|
||||||
|
|
|
@ -6,9 +6,10 @@
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from pyircbot.modulebase import ModuleBase, ModuleHook
|
from pyircbot.modulebase import ModuleBase, command, hook
|
||||||
import datetime
|
import datetime
|
||||||
from time import mktime
|
from time import mktime
|
||||||
|
from pyircbot.modules.ModInfo import info
|
||||||
|
|
||||||
|
|
||||||
class Tell(ModuleBase):
|
class Tell(ModuleBase):
|
||||||
|
@ -38,27 +39,21 @@ class Tell(ModuleBase):
|
||||||
self.db.query("DELETE FROM `tells` WHERE `when`<?",
|
self.db.query("DELETE FROM `tells` WHERE `when`<?",
|
||||||
(int(mktime(datetime.datetime.now().timetuple())) - self.config["maxage"],)).close()
|
(int(mktime(datetime.datetime.now().timetuple())) - self.config["maxage"],)).close()
|
||||||
|
|
||||||
self.hooks = [
|
@hook("PRIVMSG", "JOIN")
|
||||||
ModuleHook(["JOIN", "PRIVMSG"], self.showtell),
|
def showtell(self, msg, cmd):
|
||||||
ModuleHook(["PRIVMSG"], self.tellcmds)
|
|
||||||
]
|
|
||||||
|
|
||||||
def showtell(self, args, prefix, trailing):
|
|
||||||
prefix = self.bot.decodePrefix(prefix)
|
|
||||||
|
|
||||||
# Look for tells for this person
|
# Look for tells for this person
|
||||||
c = self.db.query("SELECT * FROM `tells` WHERE `recip`=?", (prefix.nick,))
|
c = self.db.query("SELECT * FROM `tells` WHERE `recip`=?", (msg.prefix.nick,))
|
||||||
tells = c.fetchall()
|
tells = c.fetchall()
|
||||||
c.close()
|
c.close()
|
||||||
for tell in tells:
|
for tell in tells:
|
||||||
agostr = Tell.timesince(datetime.datetime.fromtimestamp(tell["when"]))
|
agostr = Tell.timesince(datetime.datetime.fromtimestamp(tell["when"]))
|
||||||
recip = None
|
recip = None
|
||||||
if tell["channel"] == "":
|
if tell["channel"] == "":
|
||||||
recip = prefix.nick
|
recip = msg.prefix.nick
|
||||||
else:
|
else:
|
||||||
recip = tell["channel"]
|
recip = tell["channel"]
|
||||||
self.bot.act_PRIVMSG(recip, "%s: %s said %s ago: %s" % (
|
self.bot.act_PRIVMSG(recip, "%s: %s said %s ago: %s" % (
|
||||||
prefix.nick,
|
msg.prefix.nick,
|
||||||
tell["sender"],
|
tell["sender"],
|
||||||
agostr,
|
agostr,
|
||||||
tell["message"]
|
tell["message"]
|
||||||
|
@ -66,34 +61,31 @@ class Tell(ModuleBase):
|
||||||
# Delete
|
# Delete
|
||||||
self.db.query("DELETE FROM `tells` WHERE `id`=?", (tell["id"],))
|
self.db.query("DELETE FROM `tells` WHERE `id`=?", (tell["id"],))
|
||||||
|
|
||||||
def tellcmds(self, args, prefix, trailing):
|
@info(".tell <person> <message> relay a message when the target is online", cmds=["tell"])
|
||||||
prefixObj = self.bot.decodePrefix(prefix)
|
@command("tell", allow_private=True)
|
||||||
replyTo = prefixObj.nick if "#" not in args[0] else args[0]
|
def tellcmds(self, msg, cmd):
|
||||||
|
if len(cmd.args) < 2:
|
||||||
|
self.bot.act_PRIVMSG(msg.args[0], "%s: .tell <person> <message> - Tell someone something the next time "
|
||||||
|
"they're seen. Example: .tell antiroach Do your homework!" % msg.prefix.nick)
|
||||||
|
return
|
||||||
|
|
||||||
cmd = self.bot.messageHasCommand(".tell", trailing)
|
recip = cmd.args[0]
|
||||||
if cmd:
|
message = ' '.join(cmd.args[1:]).strip()
|
||||||
if len(cmd.args) < 2:
|
|
||||||
self.bot.act_PRIVMSG(replyTo, "%s: .tell <person> <message> - Tell someone something the next time "
|
|
||||||
"they're seen. Example: .tell antiroach Do your homework!" % prefixObj.nick)
|
|
||||||
return
|
|
||||||
|
|
||||||
recip = cmd.args[0]
|
if not message:
|
||||||
message = ' '.join(cmd.args[1:]).strip()
|
self.bot.act_PRIVMSG(msg.args[0], "%s: .tell <person> <message> - Tell someone something the next time "
|
||||||
|
|
||||||
if not message:
|
|
||||||
self.bot.act_PRIVMSG(replyTo, "%s: .tell <person> <message> - Tell someone something the next time "
|
|
||||||
"they're seen. Example: .tell antiroach Do your homework!" %
|
"they're seen. Example: .tell antiroach Do your homework!" %
|
||||||
prefixObj.nick)
|
msg.prefix.nick)
|
||||||
return
|
return
|
||||||
|
|
||||||
self.db.query("INSERT INTO `tells` (`sender`, `channel`, `when`, `recip`, `message`) VALUES "
|
self.db.query("INSERT INTO `tells` (`sender`, `channel`, `when`, `recip`, `message`) VALUES "
|
||||||
"(?, ?, ?, ?, ?);", (prefixObj.nick,
|
"(?, ?, ?, ?, ?);", (msg.prefix.nick,
|
||||||
args[0] if "#" in args[0] else "",
|
msg.args[0] if "#" in msg.args[0] else "",
|
||||||
int(mktime(datetime.datetime.now().timetuple())),
|
int(mktime(datetime.datetime.now().timetuple())),
|
||||||
recip,
|
recip,
|
||||||
message)).close()
|
message)).close()
|
||||||
|
|
||||||
self.bot.act_PRIVMSG(replyTo, "%s: I'll pass that along." % prefixObj.nick)
|
self.bot.act_PRIVMSG(msg.args[0], "%s: I'll pass that along." % msg.prefix.nick)
|
||||||
|
|
||||||
# Copyright (c) Django Software Foundation and individual contributors.
|
# Copyright (c) Django Software Foundation and individual contributors.
|
||||||
# All rights reserved.
|
# All rights reserved.
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
from threading import Thread
|
from threading import Thread
|
||||||
from time import sleep, time
|
from time import sleep, time
|
||||||
from pyircbot.modulebase import ModuleBase, ModuleHook
|
from pyircbot.modulebase import ModuleBase, hook
|
||||||
from random import randrange, choice
|
from random import randrange, choice
|
||||||
|
|
||||||
|
|
||||||
|
@ -15,15 +15,15 @@ class Triggered(ModuleBase):
|
||||||
def __init__(self, bot, moduleName):
|
def __init__(self, bot, moduleName):
|
||||||
ModuleBase.__init__(self, bot, moduleName)
|
ModuleBase.__init__(self, bot, moduleName)
|
||||||
self.quietuntil = time()
|
self.quietuntil = time()
|
||||||
self.hooks.append(ModuleHook("PRIVMSG", self.check))
|
|
||||||
|
|
||||||
def check(self, args):
|
@hook("PRIVMSG")
|
||||||
|
def check(self, msg, cmd):
|
||||||
if time() < self.quietuntil:
|
if time() < self.quietuntil:
|
||||||
return
|
return
|
||||||
if not args.args[0].lower() in self.config["channels"]:
|
if not msg.args[0].lower() in self.config["channels"]:
|
||||||
return
|
return
|
||||||
|
|
||||||
message = args.trailing.lower()
|
message = msg.trailing.lower()
|
||||||
triggered = False
|
triggered = False
|
||||||
for word in self.config["words"]:
|
for word in self.config["words"]:
|
||||||
if word.lower() in message:
|
if word.lower() in message:
|
||||||
|
@ -33,7 +33,7 @@ class Triggered(ModuleBase):
|
||||||
if not triggered:
|
if not triggered:
|
||||||
return
|
return
|
||||||
|
|
||||||
msg = Thread(target=self.scream, args=(args.args[0],))
|
msg = Thread(target=self.scream, args=(msg.args[0],))
|
||||||
msg.daemon = True
|
msg.daemon = True
|
||||||
msg.start()
|
msg.start()
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from pyircbot.modulebase import ModuleBase, ModuleHook
|
from pyircbot.modulebase import ModuleBase, hook
|
||||||
from random import randint
|
from random import randint
|
||||||
import time
|
import time
|
||||||
import re
|
import re
|
||||||
|
@ -19,9 +19,6 @@ class UnoPlay(ModuleBase):
|
||||||
def __init__(self, bot, moduleName):
|
def __init__(self, bot, moduleName):
|
||||||
ModuleBase.__init__(self, bot, moduleName)
|
ModuleBase.__init__(self, bot, moduleName)
|
||||||
self.servicesModule = self.bot.getmodulebyname("Services")
|
self.servicesModule = self.bot.getmodulebyname("Services")
|
||||||
self.hooks = [ModuleHook("PRIVMSG", self.unoplay),
|
|
||||||
ModuleHook("PRIVMSG", self.trigger),
|
|
||||||
ModuleHook("NOTICE", self.decklisten)]
|
|
||||||
self.current_card = None
|
self.current_card = None
|
||||||
self.shouldgo = False
|
self.shouldgo = False
|
||||||
self.has_drawn = False
|
self.has_drawn = False
|
||||||
|
@ -32,30 +29,32 @@ class UnoPlay(ModuleBase):
|
||||||
assert self.config["strategy"] in self.strategies
|
assert self.config["strategy"] in self.strategies
|
||||||
self.cards = []
|
self.cards = []
|
||||||
|
|
||||||
def trigger(self, args, prefix, trailing):
|
@hook("PRIVMSG")
|
||||||
if trailing.startswith("["): # anti-znc buffer playback
|
def trigger(self, msg, cmd):
|
||||||
|
if msg.trailing.startswith("["): # anti-znc buffer playback
|
||||||
return
|
return
|
||||||
if self.config["enable_trigger"] and "!jo" in trailing:
|
if self.config["enable_trigger"] and "!jo" in msg.trailing:
|
||||||
self.bot.act_PRIVMSG(self.config["unochannel"], "jo")
|
self.bot.act_PRIVMSG(self.config["unochannel"], "jo")
|
||||||
elif trailing == "jo":
|
elif msg.trailing == "jo":
|
||||||
# Reset streak counter & join if another human joins
|
# Reset streak counter & join if another human joins
|
||||||
self.games_played = 0
|
self.games_played = 0
|
||||||
self.join_game()
|
self.join_game()
|
||||||
|
|
||||||
def decklisten(self, args, prefix, trailing):
|
@hook("NOTICE")
|
||||||
|
def decklisten(self, msg, cmd):
|
||||||
"""
|
"""
|
||||||
Listen for messages sent via NOTICe to the bot, this is usually a list of cards in our hand. Parse them.
|
Listen for messages sent via NOTICe to the bot, this is usually a list of cards in our hand. Parse them.
|
||||||
"""
|
"""
|
||||||
if trailing.startswith("["): # anti-znc buffer playback
|
if msg.trailing.startswith("["): # anti-znc buffer playback
|
||||||
return
|
return
|
||||||
if not prefix or self.config["unobot"] not in prefix:
|
if not msg.prefix or self.config["unobot"] not in msg.prefix:
|
||||||
return
|
return
|
||||||
|
|
||||||
if "You don't have that card" in trailing:
|
if "You don't have that card" in msg.trailing:
|
||||||
self.log.error("played invalid card!")
|
self.log.error("played invalid card!")
|
||||||
return
|
return
|
||||||
|
|
||||||
trailing = self.stripcolors(trailing)
|
trailing = self.stripcolors(msg.trailing)
|
||||||
|
|
||||||
cards = []
|
cards = []
|
||||||
|
|
||||||
|
@ -73,13 +72,14 @@ class UnoPlay(ModuleBase):
|
||||||
self.shouldgo = False
|
self.shouldgo = False
|
||||||
self.taketurn()
|
self.taketurn()
|
||||||
|
|
||||||
def unoplay(self, args, prefix, trailing):
|
@hook("PRIVMSG")
|
||||||
if trailing.startswith("["): # anti-znc buffer playback
|
def unoplay(self, msg, cmd):
|
||||||
|
if msg.trailing.startswith("["): # anti-znc buffer playback
|
||||||
return
|
return
|
||||||
|
|
||||||
trailing = self.stripcolors(trailing)
|
trailing = self.stripcolors(msg.trailing)
|
||||||
|
|
||||||
if self.config["unobot"] not in prefix:
|
if self.config["unobot"] not in msg.prefix.nick:
|
||||||
return
|
return
|
||||||
|
|
||||||
# Parse card from beginning message
|
# Parse card from beginning message
|
||||||
|
|
|
@ -7,30 +7,24 @@
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from pyircbot.modulebase import ModuleBase, ModuleHook
|
from pyircbot.modulebase import ModuleBase, command
|
||||||
from requests import get
|
from requests import get
|
||||||
|
from pyircbot.modules.ModInfo import info
|
||||||
|
|
||||||
|
|
||||||
class Urban(ModuleBase):
|
class Urban(ModuleBase):
|
||||||
def __init__(self, bot, moduleName):
|
|
||||||
ModuleBase.__init__(self, bot, moduleName)
|
|
||||||
self.hooks = [ModuleHook("PRIVMSG", self.urban)]
|
|
||||||
|
|
||||||
def urban(self, args, prefix, trailing):
|
@info("urban <term> lookup an urban dictionary definition", cmds=["urban", "u"])
|
||||||
cmd = self.bot.messageHasCommand(".urban", trailing)
|
@command("urban", "u")
|
||||||
if not cmd:
|
def urban(self, msg, cmd):
|
||||||
cmd = self.bot.messageHasCommand(".u", trailing)
|
print(cmd)
|
||||||
if cmd and args[0][0:1] == "#":
|
definitions = get("http://www.urbandictionary.com/iphone/search/define",
|
||||||
if cmd.args_str.strip() == "":
|
params={"term": cmd.args_str}).json()["list"]
|
||||||
self.bot.act_PRIVMSG(args[0], ".u/.urban <phrase> -- looks up <phrase> on urbandictionary.com")
|
if len(definitions) == 0:
|
||||||
return
|
self.bot.act_PRIVMSG(msg.args[0], "Urban definition: no results!")
|
||||||
definitions = get("http://www.urbandictionary.com/iphone/search/define",
|
else:
|
||||||
params={"term": cmd.args_str}).json()["list"]
|
defstr = definitions[0]['definition'].replace('\n', ' ').replace('\r', '')
|
||||||
if len(definitions) == 0:
|
if len(defstr) > 360:
|
||||||
self.bot.act_PRIVMSG(args[0], "Urban definition: no results!")
|
defstr = defstr[0:360] + "..."
|
||||||
else:
|
self.bot.act_PRIVMSG(msg.args[0], "Urban definition: %s - http://urbanup.com/%s" %
|
||||||
defstr = definitions[0]['definition'].replace('\n', ' ').replace('\r', '')
|
(defstr, definitions[0]['defid']))
|
||||||
if len(defstr) > 360:
|
|
||||||
defstr = defstr[0:360] + "..."
|
|
||||||
self.bot.act_PRIVMSG(args[0], "Urban definition: %s - http://urbanup.com/%s" %
|
|
||||||
(defstr, definitions[0]['defid']))
|
|
||||||
|
|
|
@ -7,8 +7,9 @@
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from pyircbot.modulebase import ModuleBase, ModuleHook
|
from pyircbot.modulebase import ModuleBase, command
|
||||||
from requests import get
|
from requests import get
|
||||||
|
from pyircbot.modules.ModInfo import info
|
||||||
|
|
||||||
|
|
||||||
class Weather(ModuleBase):
|
class Weather(ModuleBase):
|
||||||
|
@ -31,80 +32,71 @@ class Weather(ModuleBase):
|
||||||
self.log.error("Weather: An 'attributes' service is required")
|
self.log.error("Weather: An 'attributes' service is required")
|
||||||
return
|
return
|
||||||
|
|
||||||
self.hooks = [ModuleHook("PRIVMSG", self.weather)]
|
@info("weather [location] display the forecast", cmds=["weather", "w"])
|
||||||
|
@command("weather", "w")
|
||||||
def weather(self, args, prefix, trailing):
|
def cmd_weather(self, msg, cmd):
|
||||||
prefixObj = self.bot.decodePrefix(prefix)
|
hasUnit = self.attr.get(msg.prefix.nick, "weather-unit")
|
||||||
fromWho = prefixObj.nick
|
|
||||||
|
|
||||||
replyTo = args[0] if "#" in args[0] else fromWho
|
|
||||||
|
|
||||||
hasUnit = self.attr.get(fromWho, "weather-unit")
|
|
||||||
if hasUnit:
|
if hasUnit:
|
||||||
hasUnit = hasUnit.upper()
|
hasUnit = hasUnit.upper()
|
||||||
|
|
||||||
cmd = self.bot.messageHasCommand([".w", ".weather"], trailing)
|
if len(cmd.args_str) > 0:
|
||||||
if cmd:
|
self.send_weather(msg.args[0], msg.prefix.nick, cmd.args_str, hasUnit)
|
||||||
if len(cmd.args_str) > 0:
|
return
|
||||||
self.send_weather(replyTo, fromWho, cmd.args_str, hasUnit)
|
|
||||||
return
|
|
||||||
|
|
||||||
weatherZip = self.attr.get(fromWho, "weather-zip")
|
weatherZip = self.attr.get(msg.prefix.nick, "weather-zip")
|
||||||
if weatherZip is None:
|
if weatherZip is None:
|
||||||
self.bot.act_PRIVMSG(replyTo, "%s: you must set a location with .setloc" % (fromWho,))
|
self.bot.act_PRIVMSG(msg.args[0], "%s: you must set a location with .setloc" % (msg.prefix.nick,))
|
||||||
return
|
return
|
||||||
|
|
||||||
self.bot.act_PRIVMSG(replyTo, "%s: %s" % (fromWho, self.getWeather(weatherZip, hasUnit)))
|
self.bot.act_PRIVMSG(msg.args[0], "%s: %s" % (msg.prefix.nick, self.getWeather(weatherZip, hasUnit)))
|
||||||
|
|
||||||
cmd = self.bot.messageHasCommand(".setloc", trailing)
|
@info("setloc <location> set your home location for weather lookups", cmds=["setloc"])
|
||||||
if cmd and not args[0] == "#":
|
@command("setloc", allow_private=True)
|
||||||
|
def cmd_setloc(self, msg, cmd):
|
||||||
|
# if not cmd.args:
|
||||||
|
# self.bot.act_PRIVMSG(fromWho, ".setloc: set your location for weather lookup. Example: "
|
||||||
|
# ".setloc Rochester, NY")
|
||||||
|
# return
|
||||||
|
reply_to = msg.args[0] if msg.args[0].startswith("#") else msg.prefix.nick
|
||||||
|
weatherLoc = cmd.args_str
|
||||||
|
try:
|
||||||
|
result = self.getWeather(weatherLoc) # NOQA
|
||||||
|
except LocationNotSpecificException as lnse:
|
||||||
|
self.bot.act_PRIVMSG(reply_to, "'%s': location not specific enough. Did you mean: %s" %
|
||||||
|
(weatherLoc, self.alternates_to_str(lnse.alternates)))
|
||||||
|
return
|
||||||
|
except LocationException as le:
|
||||||
|
self.bot.act_PRIVMSG(reply_to, "'%s': location not found: %s" % (weatherLoc, le))
|
||||||
|
return
|
||||||
|
|
||||||
if not cmd.args:
|
if not self.login.check(msg.prefix.nick, msg.prefix.hostname):
|
||||||
self.bot.act_PRIVMSG(fromWho, ".setloc: set your location for weather lookup. Example: "
|
self.bot.act_PRIVMSG(reply_to, ".setloc: you need to be logged in to do that (try .login)")
|
||||||
".setloc Rochester, NY")
|
return
|
||||||
return
|
|
||||||
|
|
||||||
weatherLoc = cmd.args_str
|
self.attr.set(msg.prefix.nick, "weather-zip", weatherLoc)
|
||||||
|
self.bot.act_PRIVMSG(reply_to, "Saved your location as %s"
|
||||||
|
% self.attr.get(msg.prefix.nick, "weather-zip"))
|
||||||
|
if self.attr.get(msg.prefix.nick, "weather-zip") is None:
|
||||||
|
self.bot.act_PRIVMSG(reply_to, "Tip: choose C or F with .wunit <C/F>")
|
||||||
|
|
||||||
try:
|
@info("wunit <c|f> set preferred weather unit", cmds=["wunit"])
|
||||||
result = self.getWeather(weatherLoc) # NOQA
|
@command("wunit", allow_private=True)
|
||||||
except LocationNotSpecificException as lnse:
|
def cmd_wunit(self, msg, cmd):
|
||||||
self.bot.act_PRIVMSG(fromWho, "'%s': location not specific enough. Did you mean: %s" %
|
if cmd.args[0].lower() not in ['c', 'f']:
|
||||||
(weatherLoc, self.alternates_to_str(lnse.alternates)))
|
return
|
||||||
return
|
unit = cmd.args[0].lower()
|
||||||
except LocationException as le:
|
reply_to = msg.args[0] if msg.args[0].startswith("#") else msg.prefix.nick
|
||||||
self.bot.act_PRIVMSG(fromWho, "'%s': location not found: %s" % (weatherLoc, le))
|
|
||||||
return
|
|
||||||
|
|
||||||
if not self.login.check(prefixObj.nick, prefixObj.hostname):
|
# if unit is None:
|
||||||
self.bot.act_PRIVMSG(fromWho, ".setloc: you need to be logged in to do that (try .login)")
|
# self.bot.act_PRIVMSG(fromWho, ".wunit: set your preferred temperature unit to C or F")
|
||||||
return
|
# return
|
||||||
|
|
||||||
self.attr.set(fromWho, "weather-zip", weatherLoc)
|
if not self.login.check(msg.prefix.nick, msg.prefix.hostname):
|
||||||
self.bot.act_PRIVMSG(fromWho, "Saved your location as %s" % self.attr.get(fromWho, "weather-zip"))
|
self.bot.act_PRIVMSG(reply_to, ".wunit: you need to be logged in to do that (try .login)")
|
||||||
if self.attr.get(fromWho, "weather-zip") is None:
|
return
|
||||||
self.bot.act_PRIVMSG(fromWho, "Tip: choose C or F with .wunit <C/F>")
|
|
||||||
|
|
||||||
cmd = self.bot.messageHasCommand(".wunit", trailing)
|
self.attr.set(msg.prefix.nick, "weather-unit", unit.lower())
|
||||||
if cmd and not args[0] == "#":
|
self.bot.act_PRIVMSG(reply_to, "Saved your preferred unit as %s" % unit)
|
||||||
unit = None
|
|
||||||
try:
|
|
||||||
assert cmd.args[0].lower() in ['c', 'f']
|
|
||||||
unit = cmd.args[0]
|
|
||||||
except:
|
|
||||||
pass
|
|
||||||
|
|
||||||
if unit is None:
|
|
||||||
self.bot.act_PRIVMSG(fromWho, ".wunit: set your preferred temperature unit to C or F")
|
|
||||||
return
|
|
||||||
|
|
||||||
if not self.login.check(prefixObj.nick, prefixObj.hostname):
|
|
||||||
self.bot.act_PRIVMSG(fromWho, ".wunit: you need to be logged in to do that (try .login)")
|
|
||||||
return
|
|
||||||
|
|
||||||
self.attr.set(fromWho, "weather-unit", unit.lower())
|
|
||||||
self.bot.act_PRIVMSG(fromWho, "Saved your preferred unit as %s" %
|
|
||||||
self.attr.get(fromWho, "weather-unit").upper())
|
|
||||||
|
|
||||||
def send_weather(self, target, hilight, location, units=None):
|
def send_weather(self, target, hilight, location, units=None):
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -7,7 +7,9 @@
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from pyircbot.modulebase import ModuleBase, ModuleHook
|
from pyircbot.modulebase import ModuleBase, command
|
||||||
|
from pyircbot.modules.ModInfo import info
|
||||||
|
from random import shuffle
|
||||||
from requests import get
|
from requests import get
|
||||||
import time
|
import time
|
||||||
import re
|
import re
|
||||||
|
@ -16,7 +18,6 @@ import re
|
||||||
class Youtube(ModuleBase):
|
class Youtube(ModuleBase):
|
||||||
def __init__(self, bot, moduleName):
|
def __init__(self, bot, moduleName):
|
||||||
ModuleBase.__init__(self, bot, moduleName)
|
ModuleBase.__init__(self, bot, moduleName)
|
||||||
self.hooks = [ModuleHook("PRIVMSG", self.youtube)]
|
|
||||||
|
|
||||||
def getISOdurationseconds(self, stamp):
|
def getISOdurationseconds(self, stamp):
|
||||||
ISO_8601_period_rx = re.compile(
|
ISO_8601_period_rx = re.compile(
|
||||||
|
@ -33,25 +34,24 @@ class Youtube(ModuleBase):
|
||||||
) # http://stackoverflow.com/a/16742742
|
) # http://stackoverflow.com/a/16742742
|
||||||
return ISO_8601_period_rx.match(stamp).groupdict()
|
return ISO_8601_period_rx.match(stamp).groupdict()
|
||||||
|
|
||||||
def youtube(self, args, prefix, trailing):
|
@info("yt search for youtube videos", cmds=["yt", "youtube"])
|
||||||
|
@command("yt", "youtube")
|
||||||
|
def youtube(self, msg, cmd):
|
||||||
|
j = get("https://www.googleapis.com/youtube/v3/search",
|
||||||
|
params={"key": self.config["api_key"],
|
||||||
|
"part": "snippet",
|
||||||
|
"type": "video",
|
||||||
|
"maxResults": "25",
|
||||||
|
"safeSearch": self.config.get("safe_search", "none"),
|
||||||
|
"q": cmd.args_str}).json()
|
||||||
|
|
||||||
cmd = self.bot.messageHasCommand(".youtube", trailing)
|
if 'error' in j or len(j["items"]) == 0:
|
||||||
if not cmd:
|
self.bot.act_PRIVMSG(msg.args[0], "No results found.")
|
||||||
cmd = self.bot.messageHasCommand(".yt", trailing)
|
else:
|
||||||
if cmd and args[0][0:1] == "#":
|
shuffle(j['items'])
|
||||||
# TODO search youtube
|
vid_id = j["items"][0]['id']['videoId']
|
||||||
if cmd.args_str.strip() == "":
|
self.bot.act_PRIVMSG(msg.args[0], "http://youtu.be/{} :: {}".format(vid_id,
|
||||||
self.bot.act_PRIVMSG(args[0], '.youtube <query> -- returns the first YouTube search result for <query>')
|
self.get_video_description(vid_id)))
|
||||||
return
|
|
||||||
j = get("http://gdata.youtube.com/feeds/api/videos?v=2&alt=jsonc&max-results=1",
|
|
||||||
params={"q": trailing}).json()
|
|
||||||
if 'error' in j or j['data']['totalItems'] == 0:
|
|
||||||
self.bot.act_PRIVMSG(args[0], "YouTube: No results found.")
|
|
||||||
else:
|
|
||||||
vid_id = j['data']['items'][0]['id']
|
|
||||||
vidinfo = self.get_video_description(vid_id)
|
|
||||||
if vidinfo:
|
|
||||||
self.bot.act_PRIVMSG(args[0], "http://youtu.be/%s :: %s" % (vid_id, vidinfo))
|
|
||||||
|
|
||||||
def get_video_description(self, vid_id):
|
def get_video_description(self, vid_id):
|
||||||
apidata = get('https://www.googleapis.com/youtube/v3/videos?part=snippet,contentDetails,statistics&id=%s'
|
apidata = get('https://www.googleapis.com/youtube/v3/videos?part=snippet,contentDetails,statistics&id=%s'
|
||||||
|
|
|
@ -10,128 +10,21 @@ import logging
|
||||||
import sys
|
import sys
|
||||||
from pyircbot.rpc import BotRPC
|
from pyircbot.rpc import BotRPC
|
||||||
from pyircbot.irccore import IRCCore
|
from pyircbot.irccore import IRCCore
|
||||||
from collections import namedtuple
|
|
||||||
from socket import AF_INET, AF_INET6
|
from socket import AF_INET, AF_INET6
|
||||||
from json import load
|
|
||||||
import os.path
|
import os.path
|
||||||
import asyncio
|
import asyncio
|
||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
|
|
||||||
ParsedCommand = namedtuple("ParsedCommand", "command args args_str message")
|
class ModuleLoader(object):
|
||||||
|
def __init__(self):
|
||||||
|
|
||||||
class PyIRCBot(object):
|
|
||||||
""":param botconfig: The configuration of this instance of the bot. Passed by main.py.
|
|
||||||
:type botconfig: dict
|
|
||||||
"""
|
|
||||||
version = "4.1.0"
|
|
||||||
|
|
||||||
def __init__(self, botconfig):
|
|
||||||
self.log = logging.getLogger('PyIRCBot')
|
|
||||||
"""Reference to logger object"""
|
|
||||||
|
|
||||||
self.loop = asyncio.get_event_loop()
|
|
||||||
|
|
||||||
"""saved copy of the instance config"""
|
|
||||||
self.botconfig = botconfig
|
|
||||||
|
|
||||||
"""storage of imported modules"""
|
"""storage of imported modules"""
|
||||||
self.modules = {}
|
self.modules = {}
|
||||||
|
|
||||||
"""instances of modules"""
|
"""instances of modules"""
|
||||||
self.moduleInstances = {}
|
self.moduleInstances = {}
|
||||||
|
|
||||||
"""Reference to BotRPC thread"""
|
self.log = logging.getLogger('ModuleLoader')
|
||||||
self.rpc = BotRPC(self)
|
|
||||||
|
|
||||||
ratelimit = self.botconfig["connection"].get("rate_limit", None) or dict(rate_max=5.0, rate_int=1.1)
|
|
||||||
|
|
||||||
"""IRC protocol handler"""
|
|
||||||
self.irc = IRCCore(servers=self.botconfig["connection"]["servers"],
|
|
||||||
loop=self.loop,
|
|
||||||
rate_limit=True if ratelimit else False,
|
|
||||||
rate_max=ratelimit["rate_max"],
|
|
||||||
rate_int=ratelimit["rate_int"])
|
|
||||||
if self.botconfig.get("connection").get("force_ipv6", False):
|
|
||||||
self.irc.connection_family = AF_INET6
|
|
||||||
elif self.botconfig.get("connection").get("force_ipv4", False):
|
|
||||||
self.irc.connection_family = AF_INET
|
|
||||||
self.irc.bind_addr = self.botconfig.get("connection").get("bind", None)
|
|
||||||
|
|
||||||
# legacy support
|
|
||||||
self.act_PONG = self.irc.act_PONG
|
|
||||||
self.act_USER = self.irc.act_USER
|
|
||||||
self.act_NICK = self.irc.act_NICK
|
|
||||||
self.act_JOIN = self.irc.act_JOIN
|
|
||||||
self.act_PRIVMSG = self.irc.act_PRIVMSG
|
|
||||||
self.act_MODE = self.irc.act_MODE
|
|
||||||
self.act_ACTION = self.irc.act_ACTION
|
|
||||||
self.act_KICK = self.irc.act_KICK
|
|
||||||
self.act_QUIT = self.irc.act_QUIT
|
|
||||||
self.act_PASS = self.irc.act_PASS
|
|
||||||
self.get_nick = self.irc.get_nick
|
|
||||||
self.decodePrefix = IRCCore.decodePrefix
|
|
||||||
|
|
||||||
# Load modules
|
|
||||||
self.initModules()
|
|
||||||
|
|
||||||
# Internal usage hook
|
|
||||||
self.irc.addHook("PRIVMSG", self._irchook_internal)
|
|
||||||
|
|
||||||
def run(self):
|
|
||||||
self.client = asyncio.ensure_future(self.irc.loop(self.loop), loop=self.loop)
|
|
||||||
try:
|
|
||||||
self.loop.set_debug(True)
|
|
||||||
self.loop.run_until_complete(self.client)
|
|
||||||
finally:
|
|
||||||
logging.debug("Escaped main loop")
|
|
||||||
|
|
||||||
def disconnect(self, message):
|
|
||||||
"""Send quit message and disconnect from IRC.
|
|
||||||
|
|
||||||
:param message: Quit message
|
|
||||||
:type message: str
|
|
||||||
:param reconnect: True causes a reconnection attempt to be made after the disconnect
|
|
||||||
:type reconnect: bool
|
|
||||||
"""
|
|
||||||
self.log.info("disconnect")
|
|
||||||
self.kill(message=message)
|
|
||||||
|
|
||||||
def kill(self, message="Help! Another thread is killing me :(", forever=True):
|
|
||||||
"""Close the connection violently
|
|
||||||
|
|
||||||
:param sys_exit: True causes sys.exit(0) to be called
|
|
||||||
:type sys_exit: bool
|
|
||||||
:param message: Quit message
|
|
||||||
:type message: str
|
|
||||||
"""
|
|
||||||
if forever:
|
|
||||||
self.closeAllModules()
|
|
||||||
asyncio.run_coroutine_threadsafe(self.irc.kill(message=message, forever=forever), self.loop)
|
|
||||||
|
|
||||||
def initModules(self):
|
|
||||||
"""load modules specified in instance config"""
|
|
||||||
" append module location to path "
|
|
||||||
sys.path.append(os.path.dirname(__file__) + "/modules/")
|
|
||||||
|
|
||||||
" append usermodule dir to beginning of path"
|
|
||||||
for path in self.botconfig["bot"]["usermodules"]:
|
|
||||||
sys.path.insert(0, path + "/")
|
|
||||||
|
|
||||||
for modulename in self.botconfig["modules"]:
|
|
||||||
self.loadmodule(modulename)
|
|
||||||
|
|
||||||
def _irchook_internal(self, msg):
|
|
||||||
"""
|
|
||||||
IRC hook handler. Calling point for IRCHook based module hooks. This method is called when a message is
|
|
||||||
received. It tests all hooks in all modules against the message can calls the hooked function on hits.
|
|
||||||
"""
|
|
||||||
for module_name, module in self.moduleInstances.items():
|
|
||||||
for hook in module.irchooks:
|
|
||||||
validation = hook.validator(msg, self)
|
|
||||||
if validation:
|
|
||||||
hook.method(msg, validation)
|
|
||||||
|
|
||||||
def importmodule(self, name):
|
def importmodule(self, name):
|
||||||
"""Import a module
|
"""Import a module
|
||||||
|
@ -189,7 +82,7 @@ class PyIRCBot(object):
|
||||||
" init the module "
|
" init the module "
|
||||||
self.moduleInstances[name] = getattr(self.modules[name], name)(self, name)
|
self.moduleInstances[name] = getattr(self.modules[name], name)(self, name)
|
||||||
" load hooks "
|
" load hooks "
|
||||||
self.loadModuleHooks(self.moduleInstances[name])
|
# self.loadModuleHooks(self.moduleInstances[name])
|
||||||
self.moduleInstances[name].onenable()
|
self.moduleInstances[name].onenable()
|
||||||
|
|
||||||
def unloadmodule(self, name):
|
def unloadmodule(self, name):
|
||||||
|
@ -201,7 +94,7 @@ class PyIRCBot(object):
|
||||||
" notify the module of disabling "
|
" notify the module of disabling "
|
||||||
self.moduleInstances[name].ondisable()
|
self.moduleInstances[name].ondisable()
|
||||||
" unload all hooks "
|
" unload all hooks "
|
||||||
self.unloadModuleHooks(self.moduleInstances[name])
|
# self.unloadModuleHooks(self.moduleInstances[name])
|
||||||
" remove & delete the instance "
|
" remove & delete the instance "
|
||||||
self.moduleInstances.pop(name)
|
self.moduleInstances.pop(name)
|
||||||
self.log.info("Module %s unloaded" % name)
|
self.log.info("Module %s unloaded" % name)
|
||||||
|
@ -246,32 +139,6 @@ class PyIRCBot(object):
|
||||||
self.loadmodule(name)
|
self.loadmodule(name)
|
||||||
return (True, None)
|
return (True, None)
|
||||||
|
|
||||||
def loadModuleHooks(self, module):
|
|
||||||
"""**Internal.** Enable (connect) hooks of a module
|
|
||||||
|
|
||||||
:param module: module object to hook in
|
|
||||||
:type module: object"""
|
|
||||||
" activate a module's hooks "
|
|
||||||
for hook in module.hooks:
|
|
||||||
if type(hook.hook) == list:
|
|
||||||
for hookcmd in hook.hook:
|
|
||||||
self.irc.addHook(hookcmd, hook.method)
|
|
||||||
else:
|
|
||||||
self.irc.addHook(hook.hook, hook.method)
|
|
||||||
|
|
||||||
def unloadModuleHooks(self, module):
|
|
||||||
"""**Internal.** Disable (disconnect) hooks of a module
|
|
||||||
|
|
||||||
:param module: module object to unhook
|
|
||||||
:type module: object"""
|
|
||||||
" remove a modules hooks "
|
|
||||||
for hook in module.hooks:
|
|
||||||
if type(hook.hook) == list:
|
|
||||||
for hookcmd in hook.hook:
|
|
||||||
self.irc.removeHook(hookcmd, hook.method)
|
|
||||||
else:
|
|
||||||
self.irc.removeHook(hook.hook, hook.method)
|
|
||||||
|
|
||||||
def getmodulebyname(self, name):
|
def getmodulebyname(self, name):
|
||||||
"""Get a module object by name
|
"""Get a module object by name
|
||||||
|
|
||||||
|
@ -305,8 +172,16 @@ class PyIRCBot(object):
|
||||||
return m[0]
|
return m[0]
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
class PrimitiveBot(ModuleLoader):
|
||||||
|
def __init__(self, botconfig):
|
||||||
|
super().__init__()
|
||||||
|
self.botconfig = botconfig
|
||||||
|
|
||||||
def closeAllModules(self):
|
def closeAllModules(self):
|
||||||
""" Deport all modules (for shutdown). Modules are unloaded in the opposite order listed in the config. """
|
"""
|
||||||
|
Deport all modules (for shutdown). Modules are unloaded in the opposite order listed in the config.
|
||||||
|
"""
|
||||||
loaded = list(self.moduleInstances.keys())
|
loaded = list(self.moduleInstances.keys())
|
||||||
loadOrder = self.botconfig["modules"]
|
loadOrder = self.botconfig["modules"]
|
||||||
loadOrder.reverse()
|
loadOrder.reverse()
|
||||||
|
@ -318,16 +193,6 @@ class PyIRCBot(object):
|
||||||
self.deportmodule(key)
|
self.deportmodule(key)
|
||||||
|
|
||||||
" Filesystem Methods "
|
" Filesystem Methods "
|
||||||
def getDataPath(self, moduleName):
|
|
||||||
"""Return the absolute path for a module's data dir
|
|
||||||
|
|
||||||
:param moduleName: the module who's data dir we want
|
|
||||||
:type moduleName: str"""
|
|
||||||
module_dir = os.path.join(self.botconfig["bot"]["datadir"], "data", moduleName)
|
|
||||||
if not os.path.exists(module_dir):
|
|
||||||
os.mkdir(module_dir)
|
|
||||||
return module_dir
|
|
||||||
|
|
||||||
def getConfigPath(self, moduleName):
|
def getConfigPath(self, moduleName):
|
||||||
"""Return the absolute path for a module's config file
|
"""Return the absolute path for a module's config file
|
||||||
|
|
||||||
|
@ -340,63 +205,140 @@ class PyIRCBot(object):
|
||||||
return "%s.json" % basepath
|
return "%s.json" % basepath
|
||||||
return None
|
return None
|
||||||
|
|
||||||
" Utility methods "
|
def getDataPath(self, moduleName):
|
||||||
@staticmethod
|
"""Return the absolute path for a module's data dir
|
||||||
def messageHasCommand(command, message, requireArgs=False):
|
|
||||||
"""Check if a message has a command with or without args in it
|
|
||||||
|
|
||||||
:param command: the command string to look for, like !ban. If a list is passed, the first match is returned.
|
:param moduleName: the module who's data dir we want
|
||||||
:type command: str or list
|
:type moduleName: str"""
|
||||||
:param message: the message string to look in, like "!ban Lil_Mac"
|
module_dir = os.path.join(self.botconfig["bot"]["datadir"], "data", moduleName)
|
||||||
|
if not os.path.exists(module_dir):
|
||||||
|
os.mkdir(module_dir)
|
||||||
|
return module_dir
|
||||||
|
|
||||||
|
|
||||||
|
class PyIRCBot(PrimitiveBot):
|
||||||
|
""":param botconfig: The configuration of this instance of the bot. Passed by main.py.
|
||||||
|
:type botconfig: dict
|
||||||
|
"""
|
||||||
|
version = "5.0.0"
|
||||||
|
|
||||||
|
def __init__(self, botconfig):
|
||||||
|
super().__init__(botconfig)
|
||||||
|
|
||||||
|
self.log = logging.getLogger('PyIRCBot')
|
||||||
|
"""Reference to logger object"""
|
||||||
|
|
||||||
|
self.loop = asyncio.get_event_loop()
|
||||||
|
|
||||||
|
"""Reference to BotRPC thread"""
|
||||||
|
self.rpc = BotRPC(self)
|
||||||
|
|
||||||
|
ratelimit = self.botconfig["connection"].get("rate_limit", None) or dict(rate_max=5.0, rate_int=1.1)
|
||||||
|
|
||||||
|
"""IRC protocol handler"""
|
||||||
|
self.irc = IRCCore(servers=self.botconfig["connection"]["servers"],
|
||||||
|
loop=self.loop,
|
||||||
|
rate_limit=True if ratelimit else False,
|
||||||
|
rate_max=ratelimit["rate_max"],
|
||||||
|
rate_int=ratelimit["rate_int"])
|
||||||
|
if self.botconfig.get("connection").get("force_ipv6", False):
|
||||||
|
self.irc.connection_family = AF_INET6
|
||||||
|
elif self.botconfig.get("connection").get("force_ipv4", False):
|
||||||
|
self.irc.connection_family = AF_INET
|
||||||
|
self.irc.bind_addr = self.botconfig.get("connection").get("bind", None)
|
||||||
|
|
||||||
|
self.act_PONG = self.irc.act_PONG
|
||||||
|
self.act_USER = self.irc.act_USER
|
||||||
|
self.act_NICK = self.irc.act_NICK
|
||||||
|
self.act_JOIN = self.irc.act_JOIN
|
||||||
|
self.act_PRIVMSG = self.irc.act_PRIVMSG
|
||||||
|
self.act_MODE = self.irc.act_MODE
|
||||||
|
self.act_ACTION = self.irc.act_ACTION
|
||||||
|
self.act_KICK = self.irc.act_KICK
|
||||||
|
self.act_QUIT = self.irc.act_QUIT
|
||||||
|
self.act_PASS = self.irc.act_PASS
|
||||||
|
self.get_nick = self.irc.get_nick
|
||||||
|
self.decodePrefix = IRCCore.decodePrefix
|
||||||
|
|
||||||
|
# Load modules
|
||||||
|
self.initModules()
|
||||||
|
|
||||||
|
# Internal usage hook
|
||||||
|
self.irc.addHook("_ALL", self._irchook_internal)
|
||||||
|
|
||||||
|
def initModules(self):
|
||||||
|
"""load modules specified in instance config"""
|
||||||
|
" append module location to path "
|
||||||
|
sys.path.append(os.path.dirname(__file__) + "/modules/")
|
||||||
|
|
||||||
|
" append usermodule dir to beginning of path"
|
||||||
|
for path in self.botconfig["bot"]["usermodules"]:
|
||||||
|
sys.path.insert(0, path + "/")
|
||||||
|
|
||||||
|
for modulename in self.botconfig["modules"]:
|
||||||
|
self.loadmodule(modulename)
|
||||||
|
|
||||||
|
def run(self):
|
||||||
|
self.client = asyncio.ensure_future(self.irc.loop(self.loop), loop=self.loop)
|
||||||
|
try:
|
||||||
|
self.loop.set_debug(True)
|
||||||
|
self.loop.run_until_complete(self.client)
|
||||||
|
finally:
|
||||||
|
logging.debug("Escaped main loop")
|
||||||
|
|
||||||
|
def disconnect(self, message):
|
||||||
|
"""Send quit message and disconnect from IRC.
|
||||||
|
|
||||||
|
:param message: Quit message
|
||||||
:type message: str
|
:type message: str
|
||||||
:param requireArgs: if true, only validate if the command use has any amount of trailing text
|
:param reconnect: True causes a reconnection attempt to be made after the disconnect
|
||||||
:type requireArgs: bool"""
|
:type reconnect: bool
|
||||||
|
|
||||||
if not type(command) == list:
|
|
||||||
command = [command]
|
|
||||||
for item in command:
|
|
||||||
cmd = PyIRCBot.messageHasCommandSingle(item, message, requireArgs)
|
|
||||||
if cmd:
|
|
||||||
return cmd
|
|
||||||
return False
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def messageHasCommandSingle(command, message, requireArgs=False):
|
|
||||||
# Check if the message at least starts with the command
|
|
||||||
messageBeginning = message[0:len(command)]
|
|
||||||
if messageBeginning != command:
|
|
||||||
return False
|
|
||||||
# Make sure it's not a subset of a longer command (ie .meme being set off by .memes)
|
|
||||||
subsetCheck = message[len(command):len(command) + 1]
|
|
||||||
if subsetCheck != " " and subsetCheck != "":
|
|
||||||
return False
|
|
||||||
|
|
||||||
# We've got the command! Do we need args?
|
|
||||||
argsStart = len(command)
|
|
||||||
args = ""
|
|
||||||
if argsStart > 0:
|
|
||||||
args = message[argsStart + 1:].strip()
|
|
||||||
|
|
||||||
if requireArgs and args == '':
|
|
||||||
return False
|
|
||||||
|
|
||||||
# Verified! Return the set.
|
|
||||||
return ParsedCommand(command,
|
|
||||||
args.split(" ") if args else [],
|
|
||||||
args,
|
|
||||||
message)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def load(filepath):
|
|
||||||
"""Return an object from the passed filepath
|
|
||||||
|
|
||||||
:param filepath: path to a json file. filename must end with .json
|
|
||||||
:type filepath: str
|
|
||||||
:Returns: | dict
|
|
||||||
"""
|
"""
|
||||||
|
self.log.info("disconnect")
|
||||||
|
self.kill(message=message)
|
||||||
|
|
||||||
if filepath.endswith(".json"):
|
def kill(self, message="Help! Another thread is killing me :(", forever=True):
|
||||||
with open(filepath, 'r') as f:
|
"""Close the connection violently
|
||||||
return load(f)
|
|
||||||
else:
|
:param sys_exit: True causes sys.exit(0) to be called
|
||||||
raise Exception("Unknown config format")
|
:type sys_exit: bool
|
||||||
|
:param message: Quit message
|
||||||
|
:type message: str
|
||||||
|
"""
|
||||||
|
if forever:
|
||||||
|
self.closeAllModules()
|
||||||
|
asyncio.run_coroutine_threadsafe(self.irc.kill(message=message, forever=forever), self.loop)
|
||||||
|
|
||||||
|
def _irchook_internal(self, msg):
|
||||||
|
"""
|
||||||
|
IRC hook handler. Calling point for IRCHook based module hooks. This method is called when any message is
|
||||||
|
received. It tests all hooks in all modules against the message can calls the hooked function on hits.
|
||||||
|
"""
|
||||||
|
for module_name, module in self.moduleInstances.items():
|
||||||
|
for hook in module.irchooks:
|
||||||
|
validation = hook.validator(msg, self)
|
||||||
|
if validation:
|
||||||
|
hook.method(msg, validation)
|
||||||
|
|
||||||
|
" Filesystem Methods "
|
||||||
|
def getConfigPath(self, moduleName):
|
||||||
|
"""Return the absolute path for a module's config file
|
||||||
|
|
||||||
|
:param moduleName: the module who's config file we want
|
||||||
|
:type moduleName: str"""
|
||||||
|
|
||||||
|
basepath = "%s/config/%s" % (self.botconfig["bot"]["datadir"], moduleName)
|
||||||
|
|
||||||
|
if os.path.exists("%s.json" % basepath):
|
||||||
|
return "%s.json" % basepath
|
||||||
|
return None
|
||||||
|
|
||||||
|
def getDataPath(self, moduleName):
|
||||||
|
"""Return the absolute path for a module's data dir
|
||||||
|
|
||||||
|
:param moduleName: the module who's data dir we want
|
||||||
|
:type moduleName: str"""
|
||||||
|
module_dir = os.path.join(self.botconfig["bot"]["datadir"], "data", moduleName)
|
||||||
|
if not os.path.exists(module_dir):
|
||||||
|
os.mkdir(module_dir)
|
||||||
|
return module_dir
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
#!/bin/bash -x
|
||||||
|
|
||||||
|
CONFPATH=${1:-examples/config.json}
|
||||||
|
|
||||||
|
export PYTHONUNBUFFERED=1
|
||||||
|
export PYTHONPATH=.
|
||||||
|
|
||||||
|
./bin/pyircbot -c $CONFPATH --debug
|
Loading…
Reference in New Issue