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`
|
||||
* Configure: `cd examples ; vim config.json data/config/Services.json`
|
||||
* Run: `pyircbot -c config.json`
|
||||
* Configure: `vim examples/config.json examples/data/config/Services.json`
|
||||
* Run: `pyircbot -c examples/config.json`
|
||||
|
||||
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 signal
|
||||
from argparse import ArgumentParser
|
||||
from pyircbot.common import load
|
||||
from pyircbot import PyIRCBot
|
||||
from json import loads
|
||||
|
||||
|
@ -30,7 +31,7 @@ if __name__ == "__main__":
|
|||
log.critical("No bot config file specified (-c). Exiting.")
|
||||
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)
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@ Contents:
|
|||
modules/_modules.rst
|
||||
rpc/_rpc.rst
|
||||
module_guide/_module_guide.rst
|
||||
module_guide/_pubsub_mode.rst
|
||||
changelog.rst
|
||||
|
||||
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
|
||||
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:
|
||||
|
||||
.. code-block:: python
|
||||
|
@ -134,30 +136,6 @@ In usage:
|
|||
4:40:17 PM <Beefpile> 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
|
||||
==============
|
||||
|
|
|
@ -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":{
|
||||
"datadir":"./data/",
|
||||
"datadir":"./examples/data/",
|
||||
"rpcbind":"0.0.0.0",
|
||||
"rpcport":1876,
|
||||
"usermodules": [ "./data/modules/" ]
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
{
|
||||
"limit": 25,
|
||||
"recv_msg": "Oh, thanks, I'll keep %(adjective)s%(item)s safe",
|
||||
"inv_msg": "\u0000\u0001ACTION is carrying %(itemlist)s\u0000\u0001",
|
||||
"swap_msg": "\u0000\u0001ACTION takes %(adjective)s%(recv_item)s but drops %(drop_item)s\u0000\u0010",
|
||||
"inv_msg": "\u0001ACTION is carrying %(itemlist)s\u0001",
|
||||
"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",
|
||||
"adjectives": [
|
||||
"some",
|
||||
|
|
|
@ -1,2 +0,0 @@
|
|||
#!/bin/sh
|
||||
pyircbot --config config.json
|
|
@ -1,5 +1,10 @@
|
|||
from time import time
|
||||
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):
|
||||
|
@ -39,3 +44,63 @@ class burstbucket(object):
|
|||
self.bucket -= 1
|
||||
return 0
|
||||
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):
|
||||
"""Defines hooks that modules can listen for events of"""
|
||||
self.hooks = [
|
||||
'_ALL',
|
||||
'_CONNECT',
|
||||
'_DISCONNECT',
|
||||
'_RECV',
|
||||
|
@ -222,7 +223,7 @@ class IRCCore(object):
|
|||
:type prefix: str
|
||||
:param trailing: data payload of the command
|
||||
:type trailing: str"""
|
||||
for hook in self.hookcalls[command]:
|
||||
for hook in self.hookcalls["_ALL"] + self.hookcalls[command]:
|
||||
try:
|
||||
if len(getargspec(hook).args) == 2:
|
||||
hook(IRCCore.packetAsObject(command, args, prefix, trailing))
|
||||
|
|
|
@ -9,7 +9,8 @@
|
|||
import re
|
||||
import os
|
||||
import logging
|
||||
from .pyircbot import PyIRCBot
|
||||
from .common import load as pload
|
||||
from .common import messageHasCommand
|
||||
|
||||
|
||||
class ModuleBase:
|
||||
|
@ -28,9 +29,6 @@ class ModuleBase:
|
|||
self.bot = bot
|
||||
"""Reference to the master PyIRCBot object"""
|
||||
|
||||
self.hooks = []
|
||||
"""Low-level protocol hooks this module has"""
|
||||
|
||||
self.irchooks = []
|
||||
"""IRC Hooks this module has"""
|
||||
|
||||
|
@ -73,7 +71,7 @@ class ModuleBase:
|
|||
if not self.config:
|
||||
configPath = self.getConfigPath()
|
||||
if configPath is not None:
|
||||
self.config = PyIRCBot.load(configPath)
|
||||
self.config = pload(configPath)
|
||||
|
||||
def onenable(self):
|
||||
"""Called when the module is enabled"""
|
||||
|
@ -228,7 +226,7 @@ class command(hook):
|
|||
"""
|
||||
if not super().validate(msg, bot):
|
||||
return False
|
||||
if not self.allow_private and msg.args[0] == "#":
|
||||
if msg.args[0][0] != "#" and not self.allow_private:
|
||||
return False
|
||||
for keyword in self.keywords:
|
||||
single = self._validate_one(msg, keyword)
|
||||
|
@ -238,7 +236,7 @@ class command(hook):
|
|||
|
||||
def _validate_one(self, msg, 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):
|
||||
|
|
|
@ -13,7 +13,6 @@ from pyircbot.modulebase import ModuleBase
|
|||
class AttributeStorage(ModuleBase):
|
||||
def __init__(self, bot, moduleName):
|
||||
ModuleBase.__init__(self, bot, moduleName)
|
||||
self.hooks = []
|
||||
self.services = ["attributes"]
|
||||
self.db = None
|
||||
serviceProviders = self.bot.getmodulesbyservice("mysql")
|
||||
|
|
|
@ -13,7 +13,6 @@ from pyircbot.modulebase import ModuleBase
|
|||
class AttributeStorageLite(ModuleBase):
|
||||
def __init__(self, bot, moduleName):
|
||||
ModuleBase.__init__(self, bot, moduleName)
|
||||
self.hooks = []
|
||||
self.services = ["attributes"]
|
||||
self.db = None
|
||||
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 time import time
|
||||
|
||||
|
@ -18,25 +20,23 @@ class BitcoinPrice(ModuleBase):
|
|||
self.cache = None
|
||||
self.cacheAge = 0
|
||||
|
||||
self.hooks = [
|
||||
ModuleHook(["PRIVMSG"], self.btc)
|
||||
]
|
||||
@info("btc retrieve the current price of bitcoin", cmds=["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):
|
||||
prefix = self.bot.decodePrefix(prefix)
|
||||
replyTo = prefix.nick if "#" not in args[0] else args[0]
|
||||
|
||||
cmd = self.bot.messageHasCommand([".btc", ".bitcoin"], trailing)
|
||||
if cmd:
|
||||
data = self.getApi()
|
||||
self.bot.act_PRIVMSG(replyTo, "%s: %s" % (
|
||||
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'])
|
||||
msg.prefix.nick,
|
||||
"\x02\x0307Bitcoin:\x03\x02 \x0307${price:.2f}\x0f - "
|
||||
"24h change: \x0307${change:.2f}\x0f - "
|
||||
"24h volume: \x0307${volume:.0f}M\x0f".format(price=Decimal(data["price_usd"]),
|
||||
change=Decimal(data["percent_change_24h"]),
|
||||
volume=Decimal(data["24h_volume_usd"]) / 10**6)
|
||||
))
|
||||
|
||||
def getApi(self):
|
||||
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()
|
||||
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
|
||||
import datetime
|
||||
import time
|
||||
|
@ -93,7 +93,8 @@ class Calc(ModuleBase):
|
|||
|
||||
if word and changeit:
|
||||
# Add a new calc or delete
|
||||
if self.config["allowDelete"] and not value:
|
||||
if not value:
|
||||
if self.config["allowDelete"]:
|
||||
result = self.deleteCalc(channel, word)
|
||||
if result:
|
||||
self.bot.act_PRIVMSG(channel, "Calc deleted, %s." % sender)
|
||||
|
|
|
@ -15,7 +15,6 @@ from threading import Thread
|
|||
class CryptoWalletRPC(ModuleBase):
|
||||
def __init__(self, bot, moduleName):
|
||||
ModuleBase.__init__(self, bot, moduleName)
|
||||
self.hooks = []
|
||||
self.services = ["bitcoinrpc"]
|
||||
self.rpcservices = {}
|
||||
self.loadrpcservices()
|
||||
|
|
|
@ -14,7 +14,6 @@ from bitcoinrpc.authproxy import AuthServiceProxy
|
|||
class DogeRPC(ModuleBase):
|
||||
def __init__(self, bot, moduleName):
|
||||
ModuleBase.__init__(self, bot, moduleName)
|
||||
self.hooks = []
|
||||
self.services = ["dogerpc"]
|
||||
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 json
|
||||
import random
|
||||
|
@ -18,7 +19,6 @@ import os
|
|||
class DuckHunt(ModuleBase):
|
||||
def __init__(self, bot, moduleName):
|
||||
ModuleBase.__init__(self, bot, moduleName)
|
||||
self.hooks = [ModuleHook("PRIVMSG", self.hunt)]
|
||||
|
||||
self.jsonPath = self.getFilePath("scores.json")
|
||||
|
||||
|
@ -29,13 +29,11 @@ class DuckHunt(ModuleBase):
|
|||
|
||||
self.startHunt()
|
||||
|
||||
def hunt(self, args, prefix, trailing):
|
||||
prefixObj = self.bot.decodePrefix(prefix)
|
||||
fromWho = prefixObj.nick
|
||||
|
||||
cmd = self.bot.messageHasCommand("!huntscore", trailing, False)
|
||||
if cmd:
|
||||
@info("huntscore show your duckhunt score", cmds=["huntscore"])
|
||||
@command("huntscore", allow_private=True)
|
||||
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:
|
||||
|
@ -62,16 +60,12 @@ class DuckHunt(ModuleBase):
|
|||
self.bot.act_PRIVMSG(fromWho, "%s prime catches, %s runts, %s bullets used and %s misses." %
|
||||
(prime, runts, shots, misses))
|
||||
# self.bot.act_PRIVMSG(fromWho, "More info & highscores: http://duckhunt.xmopx.net/")
|
||||
self.log.info("DuckHunt: %s used !huntscore" % fromWho)
|
||||
return
|
||||
|
||||
# Channel only
|
||||
if not args[0][0] == "#":
|
||||
return
|
||||
|
||||
cmd = self.bot.messageHasCommand("!shoot", trailing, False)
|
||||
if cmd:
|
||||
@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
|
||||
|
@ -131,7 +125,7 @@ class DuckHunt(ModuleBase):
|
|||
delay = self.config["delayMin"] + random.randint(0, self.config["delayMax"] - self.config["delayMin"])
|
||||
self.timer = Timer(delay, self.duckOut)
|
||||
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):
|
||||
self.isDuckOut = True
|
||||
|
@ -143,12 +137,6 @@ class DuckHunt(ModuleBase):
|
|||
weight = float(weight) * random.random()
|
||||
return round(weight + minW, 2)
|
||||
|
||||
def getScoreFor(self, playername):
|
||||
return 1
|
||||
|
||||
def getScoresFor(self, playername):
|
||||
return 1
|
||||
|
||||
def addKillFor(self, playername, kill):
|
||||
scores = self.loadScores()
|
||||
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
|
||||
|
||||
|
||||
class Inventory(ModuleBase):
|
||||
def __init__(self, bot, moduleName):
|
||||
ModuleBase.__init__(self, bot, moduleName)
|
||||
self.hooks = []
|
||||
self.db = None
|
||||
serviceProviders = self.bot.getmodulesbyservice("sqlite")
|
||||
if not serviceProviders:
|
||||
|
@ -33,42 +33,38 @@ class Inventory(ModuleBase):
|
|||
) ;""")
|
||||
c.close()
|
||||
|
||||
self.hooks = [ModuleHook("PRIVMSG", self.checkInv)]
|
||||
|
||||
def checkInv(self, args, prefix, trailing):
|
||||
if not args[0][0] == "#":
|
||||
return
|
||||
prefixObj = self.bot.decodePrefix(prefix)
|
||||
cmd = self.bot.messageHasCommand([".have"], trailing, True)
|
||||
if cmd:
|
||||
@info("have <item> give the bot an item", cmds=["have"])
|
||||
@command("have")
|
||||
def checkInv(self, msg, cmd):
|
||||
if len(cmd.args) < 1:
|
||||
return
|
||||
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 cmd.args[0] in self.config["adjectives"]:
|
||||
newItem = cmd.args_str[len(cmd.args[0]):].strip()
|
||||
adjective = cmd.args[0]
|
||||
|
||||
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
|
||||
|
||||
dropped = self.add_item(prefixObj.nick, newItem)
|
||||
dropped = self.add_item(msg.prefix.nick, newItem)
|
||||
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 "",
|
||||
"recv_item": newItem, "drop_item": ", ".join(dropped)})
|
||||
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 "})
|
||||
|
||||
cmd = self.bot.messageHasCommand([".inventory", ".inv"], trailing)
|
||||
if cmd:
|
||||
@info("inventory show the bot's inventory", cmds=["inventory", "inv"])
|
||||
@command("inventory", "inv")
|
||||
def cmd_inv(self, msg, cmd):
|
||||
inv = self.getinventory()
|
||||
if len(inv) == 0:
|
||||
self.bot.act_PRIVMSG(args[0], self.config["inv_msg"] % {"itemlist": "nothing!"})
|
||||
self.bot.act_PRIVMSG(msg.args[0], self.config["inv_msg"] % {"itemlist": "nothing!"})
|
||||
else:
|
||||
self.bot.act_PRIVMSG(args[0], self.config["inv_msg"] % {"itemlist": ", ".join(inv)})
|
||||
self.bot.act_PRIVMSG(msg.args[0], self.config["inv_msg"] % {"itemlist": ", ".join(inv)})
|
||||
|
||||
def has_item(self, itemName):
|
||||
c = self.db.query("SELECT COUNT(*) as `num` FROM `inventory` WHERE `item`=? COLLATE NOCASE", (itemName,))
|
||||
|
|
|
@ -7,24 +7,20 @@
|
|||
"""
|
||||
|
||||
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="
|
||||
|
||||
|
||||
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):
|
||||
channel = args[0]
|
||||
prefix = self.bot.decodePrefix(prefix)
|
||||
if self.bot.messageHasCommand(".lmgtfy", trailing):
|
||||
message = trailing.split(" ")[1:]
|
||||
@info("lmgtfy <term> display a condescending internet query", cmds=["lmgtfy"])
|
||||
@command("lmgtfy", require_args=True)
|
||||
def handleMessage(self, msg, cmd):
|
||||
message = msg.trailing.split(" ")[1:]
|
||||
link = self.createLink(message)
|
||||
self.bot.act_PRIVMSG(channel, "%s: %s" % (prefix.nick, link))
|
||||
self.bot.act_PRIVMSG(msg.args[0], "%s: %s" % (msg.prefix.nick, link))
|
||||
|
||||
def createLink(self, message):
|
||||
finalUrl = BASE_URL
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
"""
|
||||
|
||||
from pyircbot.modulebase import ModuleBase, ModuleHook
|
||||
from pyircbot.modulebase import ModuleBase, hook
|
||||
from requests import get
|
||||
import re
|
||||
import time
|
||||
|
@ -22,16 +22,14 @@ class LinkTitler(ModuleBase):
|
|||
def __init__(self, bot, moduleName):
|
||||
ModuleBase.__init__(self, bot, moduleName)
|
||||
self.REQUEST_SIZE_LIMIT = 10 * 1024
|
||||
self.hooks = [ModuleHook("PRIVMSG", self.searches)]
|
||||
|
||||
def searches(self, args, prefix, trailing):
|
||||
t = Thread(target=self.doLinkTitle, args=(args, prefix, trailing))
|
||||
@hook("PRIVMSG")
|
||||
def searches(self, msg, cmd):
|
||||
t = Thread(target=self.doLinkTitle, args=(msg.args, msg.prefix.nick, msg.trailing))
|
||||
t.daemon = True
|
||||
t.start()
|
||||
|
||||
def doLinkTitle(self, args, prefix, trailing):
|
||||
sender = self.bot.decodePrefix(prefix)
|
||||
|
||||
def doLinkTitle(self, args, sender, trailing):
|
||||
# Youtube
|
||||
matches = re.compile(r'(?:youtube.*?(?:v=|/v/)|youtu\.be/|yooouuutuuube.*?id=)([-_a-z0-9]+)', re.I) \
|
||||
.findall(trailing)
|
||||
|
@ -67,7 +65,7 @@ class LinkTitler(ModuleBase):
|
|||
"domain": submission.domain,
|
||||
"nsfw": "[NSFW]" if submission.over_18 else "",
|
||||
"points": submission.ups,
|
||||
"percent": "%s%%" % int(submission.upvote_ratio *100),
|
||||
"percent": "%s%%" % int(submission.upvote_ratio * 100),
|
||||
"comments": submission.num_comments,
|
||||
"author": submission.author.name,
|
||||
"date": datetime.datetime.fromtimestamp(submission.created).strftime("%Y.%m.%d")
|
||||
|
@ -98,11 +96,11 @@ class LinkTitler(ModuleBase):
|
|||
# Fetch HTML title
|
||||
title = self.url_htmltitle(match[0])
|
||||
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:
|
||||
# Unknown types, just print type and size
|
||||
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
|
||||
"Content-Length" in headers else "unknown size"))
|
||||
|
||||
|
|
|
@ -34,8 +34,16 @@ class info(object):
|
|||
self.commands = cmds or []
|
||||
|
||||
def __call__(self, func):
|
||||
setattr(func, "irchelp", self.docstring)
|
||||
setattr(func, "irchelpc", self.commands)
|
||||
if hasattr(func, "irchelp"):
|
||||
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
|
||||
|
||||
|
||||
|
@ -77,5 +85,6 @@ class ModInfo(ModuleBase):
|
|||
for attr_name in dir(module):
|
||||
attr = getattr(module, attr_name)
|
||||
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()
|
||||
|
|
|
@ -19,7 +19,6 @@ except:
|
|||
class MySQL(ModuleBase):
|
||||
def __init__(self, bot, moduleName):
|
||||
ModuleBase.__init__(self, bot, moduleName)
|
||||
self.hooks = []
|
||||
self.services = ["mysql"]
|
||||
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 requests import get
|
||||
from lxml import etree
|
||||
|
@ -19,14 +20,9 @@ class NFLLive(ModuleBase):
|
|||
self.cache = None
|
||||
self.cacheAge = 0
|
||||
|
||||
self.hooks = [ModuleHook(["PRIVMSG"], self.nflitup)]
|
||||
|
||||
def nflitup(self, args, prefix, trailing):
|
||||
prefix = self.bot.decodePrefix(prefix)
|
||||
replyTo = prefix.nick if "#" not in args[0] else args[0]
|
||||
|
||||
cmd = self.bot.messageHasCommand(".nfl", trailing)
|
||||
if cmd:
|
||||
@info("nfl show nfl schedule & score", cmds=["nfl"])
|
||||
@command("nfl")
|
||||
def nflitup(self, message, cmd):
|
||||
games = self.getNflGamesCached()
|
||||
msg = []
|
||||
|
||||
|
@ -124,7 +120,7 @@ class NFLLive(ModuleBase):
|
|||
while not piece[-1:] == "," and len(msg) > 0:
|
||||
piece += msg[0:1]
|
||||
msg = msg[1:]
|
||||
self.bot.act_PRIVMSG(replyTo, "%s: %s" % (prefix.nick, piece.strip()))
|
||||
self.bot.act_PRIVMSG(message.args[0], "%s: %s" % (message.prefix.nick, piece.strip()))
|
||||
|
||||
def formatGameLive(self, game):
|
||||
c_vis = 3 if int(game["visitor_score"]) > int(game["home_score"]) else 4
|
||||
|
|
|
@ -7,14 +7,14 @@
|
|||
|
||||
"""
|
||||
|
||||
from pyircbot.modulebase import ModuleBase, ModuleHook
|
||||
import hashlib
|
||||
from pyircbot.modulebase import ModuleBase, hook
|
||||
from pyircbot.common import messageHasCommand
|
||||
from pyircbot.modules.ModInfo import info
|
||||
|
||||
|
||||
class NickUser(ModuleBase):
|
||||
def __init__(self, bot, moduleName):
|
||||
ModuleBase.__init__(self, bot, moduleName)
|
||||
self.hooks = [ModuleHook("PRIVMSG", self.gotmsg)]
|
||||
self.services = ["login"]
|
||||
|
||||
def check(self, nick, hostname):
|
||||
|
@ -28,17 +28,19 @@ class NickUser(ModuleBase):
|
|||
pass
|
||||
# TODO: log out all users
|
||||
|
||||
def gotmsg(self, args, prefix, trailing):
|
||||
channel = args[0]
|
||||
if channel[0] == "#":
|
||||
@hook("PRIVMSG")
|
||||
def gotmsg(self, msg, cmd):
|
||||
if msg.args[0][0] == "#":
|
||||
# Ignore channel messages
|
||||
pass
|
||||
return
|
||||
else:
|
||||
self.handlePm(args, prefix, trailing)
|
||||
self.handlePm(msg.prefix, msg.trailing)
|
||||
|
||||
def handlePm(self, args, prefix, trailing):
|
||||
prefix = self.bot.decodePrefix(prefix)
|
||||
cmd = self.bot.messageHasCommand(".setpass", trailing)
|
||||
@info("setpass [<oldpass>] <password> set or change password", cmds=["setpass"])
|
||||
@info("login <password> authenticate with the bot", cmds=["login"])
|
||||
@info("logout log out of the bot", cmds=["logout"])
|
||||
def handlePm(self, prefix, trailing):
|
||||
cmd = messageHasCommand(".setpass", trailing)
|
||||
if cmd:
|
||||
if len(cmd.args) == 0:
|
||||
self.bot.act_PRIVMSG(prefix.nick, ".setpass: usage: \".setpass newpass\" or "
|
||||
|
@ -61,7 +63,7 @@ class NickUser(ModuleBase):
|
|||
self.bot.act_PRIVMSG(prefix.nick,
|
||||
".setpass: You must provide the old password when setting a new one.")
|
||||
|
||||
cmd = self.bot.messageHasCommand(".login", trailing)
|
||||
cmd = messageHasCommand(".login", trailing)
|
||||
if cmd:
|
||||
attr = self.bot.getBestModuleForService("attributes")
|
||||
userpw = attr.getKey(prefix.nick, "password")
|
||||
|
@ -78,7 +80,7 @@ class NickUser(ModuleBase):
|
|||
self.bot.act_PRIVMSG(prefix.nick, ".login: incorrect password.")
|
||||
else:
|
||||
self.bot.act_PRIVMSG(prefix.nick, ".login: usage: \".login password\"")
|
||||
cmd = self.bot.messageHasCommand(".logout", trailing)
|
||||
cmd = messageHasCommand(".logout", trailing)
|
||||
if cmd:
|
||||
attr = self.bot.getBestModuleForService("attributes")
|
||||
loggedin = attr.getKey(prefix.nick, "loggedinfrom")
|
||||
|
@ -87,8 +89,3 @@ class NickUser(ModuleBase):
|
|||
else:
|
||||
attr.setKey(prefix.nick, "loggedinfrom", None)
|
||||
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 threading import Thread
|
||||
from pyircbot.modulebase import ModuleBase, ModuleHook
|
||||
from pyircbot.modulebase import ModuleBase, hook
|
||||
|
||||
|
||||
class PingResponder(ModuleBase):
|
||||
def __init__(self, bot, moduleName):
|
||||
ModuleBase.__init__(self, bot, moduleName)
|
||||
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"""
|
||||
# got a ping? send it right back
|
||||
self.bot.act_PONG(trailing)
|
||||
self.log.info("%s Responded to a ping: %s" % (self.bot.get_nick(), trailing))
|
||||
self.bot.act_PONG(msg.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"""
|
||||
self.timer.reset()
|
||||
|
||||
|
@ -37,7 +34,9 @@ class PingResponder(ModuleBase):
|
|||
|
||||
|
||||
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):
|
||||
Thread.__init__(self)
|
||||
self.daemon = True
|
||||
|
@ -47,11 +46,15 @@ class PingRespondTimer(Thread):
|
|||
self.start()
|
||||
|
||||
def reset(self):
|
||||
"Reset the internal ping timeout counter"
|
||||
"""
|
||||
Reset the internal ping timeout counter
|
||||
"""
|
||||
self.lastping = time()
|
||||
|
||||
def disable(self):
|
||||
"Allow the thread to die"
|
||||
"""
|
||||
Allow the thread to die
|
||||
"""
|
||||
self.alive = False
|
||||
|
||||
def run(self):
|
||||
|
|
|
@ -62,28 +62,28 @@ class PubSubClient(ModuleBase):
|
|||
self.bus.pub(self.config.get("publish").format(subchannel), "{} {}".format("default", message))
|
||||
|
||||
@hook("PRIVMSG")
|
||||
def bus_privmsg(self, msg):
|
||||
def bus_privmsg(self, msg, cmd):
|
||||
"""
|
||||
Relay a privmsg to the event bus
|
||||
"""
|
||||
self.publish("privmsg", dumps([msg.args, msg.prefix[0], msg.trailing, {"prefix": msg.prefix}]))
|
||||
|
||||
@hook("JOIN")
|
||||
def bus_join(self, msg):
|
||||
def bus_join(self, msg, cmd):
|
||||
"""
|
||||
Relay a join message to the event bus
|
||||
"""
|
||||
self.publish("join", dumps([msg.prefix[0], msg.trailing, {"prefix": msg.prefix}]))
|
||||
|
||||
@hook("PART")
|
||||
def bus_part(self, msg):
|
||||
def bus_part(self, msg, cmd):
|
||||
"""
|
||||
Relay a part message to the event bus
|
||||
"""
|
||||
self.publish("part", dumps([msg.args, msg.prefix[0], msg.trailing, {"prefix": msg.prefix}]))
|
||||
|
||||
@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
|
||||
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 pyircbot.modules.ModInfo import info
|
||||
|
||||
|
||||
class RandQuote(ModuleBase):
|
||||
def __init__(self, bot, moduleName):
|
||||
ModuleBase.__init__(self, bot, moduleName)
|
||||
self.hooks = []
|
||||
self.db = None
|
||||
serviceProviders = self.bot.getmodulesbyservice("sqlite")
|
||||
if not serviceProviders:
|
||||
|
@ -33,26 +33,19 @@ class RandQuote(ModuleBase):
|
|||
) ;""")
|
||||
c.close()
|
||||
|
||||
self.hooks = [ModuleHook("PRIVMSG", self.logquote),
|
||||
ModuleHook("PRIVMSG", self.fetchquotes)]
|
||||
|
||||
def fetchquotes(self, args, prefix, trailing):
|
||||
if not args[0][0] == "#":
|
||||
return
|
||||
cmd = self.bot.messageHasCommand([".randomquote", ".randquote", ".rq"], trailing)
|
||||
if cmd:
|
||||
@info("randquote print a random quote", cmds=["randquote", "randomquote", "rq"])
|
||||
@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(args[0], "<%s> %s" % (row["sender"], row["message"],))
|
||||
self.bot.act_PRIVMSG(msg.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)
|
||||
@hook("PRIVMSG")
|
||||
def logquote(self, msg, cmd):
|
||||
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
|
||||
c = self.db.query("SELECT * FROM `chat` ORDER BY `date` DESC LIMIT %s, 1000000;" % self.config["limit"])
|
||||
while True:
|
||||
|
|
|
@ -6,12 +6,13 @@
|
|||
|
||||
"""
|
||||
|
||||
from pyircbot.modulebase import ModuleBase, ModuleHook
|
||||
from pyircbot.modulebase import ModuleBase, command
|
||||
from datetime import datetime, timedelta
|
||||
from threading import Thread
|
||||
from time import sleep
|
||||
import re
|
||||
import pytz
|
||||
from pyircbot.modules.ModInfo import info
|
||||
|
||||
|
||||
class Remind(ModuleBase):
|
||||
|
@ -37,9 +38,6 @@ class Remind(ModuleBase):
|
|||
) ;""")
|
||||
c.close()
|
||||
|
||||
self.hooks = [ModuleHook("PRIVMSG", self.remindin),
|
||||
ModuleHook("PRIVMSG", self.remindat)]
|
||||
|
||||
self.disabled = False
|
||||
|
||||
# Start monitor thread
|
||||
|
@ -108,17 +106,12 @@ class Remind(ModuleBase):
|
|||
def ondisable(self):
|
||||
self.disabled = True
|
||||
|
||||
def remindat(self, args, prefix, trailing):
|
||||
prefixObj = self.bot.decodePrefix(prefix)
|
||||
|
||||
replyTo = prefixObj.nick if "#" not in args[0] else args[0]
|
||||
|
||||
# Lots of code borrowed from https://github.com/embolalia/willie/blob/master/willie/modules/remind.py
|
||||
cmd = self.bot.messageHasCommand([".at", ".remind"], trailing, True)
|
||||
if cmd:
|
||||
@info("remind <time> have the bot remind you", cmds=["remind", "at"])
|
||||
@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()
|
||||
|
@ -132,7 +125,7 @@ class Remind(ModuleBase):
|
|||
|
||||
except:
|
||||
self.bot.act_PRIVMSG(replyTo, "%s: .at - Remind at a time. Example: .at 20:45EST Do your homework!" %
|
||||
prefixObj.nick)
|
||||
msg.prefix.nick)
|
||||
return
|
||||
|
||||
now = datetime.now()
|
||||
|
@ -143,7 +136,7 @@ class Remind(ModuleBase):
|
|||
try:
|
||||
theirzone = pytz.timezone(Remind.translateZoneStr(tz))
|
||||
except:
|
||||
self.bot.act_PRIVMSG(replyTo, "%s: I don't recognize the timezone '%s'." % (prefixObj.nick, tz))
|
||||
self.bot.act_PRIVMSG(replyTo, "%s: I don't recognize the timezone '%s'." % (msg.prefix.nick, tz))
|
||||
return
|
||||
remindAt = theirzone.localize(remindAt, is_dst=Remind.is_dst(theirzone))
|
||||
|
||||
|
@ -162,7 +155,7 @@ class Remind(ModuleBase):
|
|||
try:
|
||||
theirzone = pytz.timezone(Remind.translateZoneStr(tz))
|
||||
except:
|
||||
self.bot.act_PRIVMSG(replyTo, "%s: I don't recognize the timezone '%s'." % (prefixObj.nick, tz))
|
||||
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)
|
||||
|
||||
|
@ -176,8 +169,8 @@ class Remind(ModuleBase):
|
|||
|
||||
# Save the reminder
|
||||
c = self.db.query("INSERT INTO `reminders` (`sender`, `senderch`, `when`, `message`) VALUES (?, ?, ?, ?)", (
|
||||
prefixObj.nick,
|
||||
args[0] if "#" in args[0] else "",
|
||||
msg.prefix.nick,
|
||||
msg.args[0] if "#" in msg.args[0] else "",
|
||||
remindAt,
|
||||
message
|
||||
))
|
||||
|
@ -187,7 +180,7 @@ class Remind(ModuleBase):
|
|||
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))
|
||||
(msg.prefix.nick, diffHours, diffMins))
|
||||
|
||||
@staticmethod
|
||||
def is_dst(tz):
|
||||
|
@ -205,21 +198,20 @@ class Remind(ModuleBase):
|
|||
else:
|
||||
return zonestr
|
||||
|
||||
def remindin(self, args, prefix, trailing):
|
||||
prefixObj = self.bot.decodePrefix(prefix)
|
||||
replyTo = prefixObj.nick if "#" not in args[0] else args[0]
|
||||
@info("after <duration> have the bot remind after", cmds=["after", "in"])
|
||||
@command("after", "in", allow_private=True)
|
||||
def remindin(self, msg, cmd):
|
||||
replyTo = msg.args[0]
|
||||
|
||||
cmd = self.bot.messageHasCommand([".in", ".after"], trailing, True)
|
||||
if cmd:
|
||||
if not cmd.args:
|
||||
self.bot.act_PRIVMSG(replyTo, "%s: .in - Remind after x amount of time. Example: .in 1week5d2h1m Go "
|
||||
"fuck yourself" % prefixObj.nick)
|
||||
"fuck yourself" % msg.prefix.nick)
|
||||
return
|
||||
|
||||
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" % prefixObj.nick)
|
||||
"fuck yourself" % msg.prefix.nick)
|
||||
return
|
||||
|
||||
delaySeconds = 0
|
||||
|
@ -227,7 +219,7 @@ class Remind(ModuleBase):
|
|||
# ('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]))
|
||||
(msg.prefix.nick, match[1]))
|
||||
return
|
||||
|
||||
delaySeconds += (Remind.scaling[match[1]] * int(match[0]))
|
||||
|
@ -235,8 +227,8 @@ class Remind(ModuleBase):
|
|||
remindAt = datetime.now() + timedelta(seconds=delaySeconds)
|
||||
|
||||
self.db.query("INSERT INTO `reminders` (`sender`, `senderch`, `when`, `message`) VALUES (?, ?, ?, ?)", (
|
||||
prefixObj.nick,
|
||||
args[0] if "#" in args[0] else "",
|
||||
msg.prefix.nick,
|
||||
msg.args[0] if "#" in msg.args[0] else "",
|
||||
remindAt,
|
||||
cmd.args_str[len(cmd.args[0]):].strip()
|
||||
)).close()
|
||||
|
@ -244,7 +236,7 @@ class Remind(ModuleBase):
|
|||
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))
|
||||
self.bot.act_PRIVMSG(replyTo, "%s: Ok, talk to you in approx %sh%sm" % (msg.prefix.nick, hours, minutes))
|
||||
|
||||
scaling = {
|
||||
"years": 365.25 * 24 * 3600,
|
||||
|
|
|
@ -156,7 +156,9 @@ class SMS(ModuleBase):
|
|||
try:
|
||||
self.twilio.api.account.messages.create(to=self.config["contacts"][contact],
|
||||
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:
|
||||
self.bot.act_PRIVMSG(msg.args[0], "Could not send message: {}".format(repr(e)))
|
||||
else:
|
||||
|
|
|
@ -14,7 +14,6 @@ import sqlite3
|
|||
class SQLite(ModuleBase):
|
||||
def __init__(self, bot, moduleName):
|
||||
ModuleBase.__init__(self, bot, moduleName)
|
||||
self.hooks = []
|
||||
self.services = ["sqlite"]
|
||||
|
||||
def opendb(self, dbname):
|
||||
|
|
|
@ -7,15 +7,15 @@
|
|||
|
||||
"""
|
||||
|
||||
from pyircbot.modulebase import ModuleBase, ModuleHook
|
||||
from pyircbot.modulebase import ModuleBase, hook
|
||||
|
||||
|
||||
class ServerPassword(ModuleBase):
|
||||
def __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"""
|
||||
if "password" in self.config and self.config["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
|
||||
|
||||
|
||||
class Services(ModuleBase):
|
||||
def __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.do_ghost = False
|
||||
|
||||
def doConnect(self, args, prefix, trailing):
|
||||
@hook("_CONNECT")
|
||||
def doConnect(self, msg, cmd):
|
||||
"""Hook for when the IRC conneciton is opened"""
|
||||
self.bot.act_NICK(self.config["user"]["nick"][0])
|
||||
self.bot.act_USER(self.config["user"]["username"], self.config["user"]["hostname"],
|
||||
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"""
|
||||
if self.config["ident"]["ghost"]:
|
||||
self.do_ghost = True
|
||||
|
@ -37,7 +35,8 @@ class Services(ModuleBase):
|
|||
return
|
||||
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"""
|
||||
if self.do_ghost:
|
||||
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.do_initservices()
|
||||
|
||||
def invited(self, args, prefix, trailing):
|
||||
@hook("INVITE")
|
||||
def invited(self, msg, cmd):
|
||||
"""Hook responding to INVITE channel invitations"""
|
||||
if trailing.lower() in self.config["privatechannels"]["list"]:
|
||||
self.log.info("Invited to %s, joining" % trailing)
|
||||
self.bot.act_JOIN(trailing)
|
||||
if msg.trailing.lower() in self.config["privatechannels"]["list"]:
|
||||
self.log.info("Invited to %s, joining" % msg.trailing)
|
||||
self.bot.act_JOIN(msg.trailing)
|
||||
|
||||
def do_initservices(self):
|
||||
"""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
|
||||
from time import mktime
|
||||
from pyircbot.modules.ModInfo import info
|
||||
|
||||
|
||||
class Tell(ModuleBase):
|
||||
|
@ -38,27 +39,21 @@ class Tell(ModuleBase):
|
|||
self.db.query("DELETE FROM `tells` WHERE `when`<?",
|
||||
(int(mktime(datetime.datetime.now().timetuple())) - self.config["maxage"],)).close()
|
||||
|
||||
self.hooks = [
|
||||
ModuleHook(["JOIN", "PRIVMSG"], self.showtell),
|
||||
ModuleHook(["PRIVMSG"], self.tellcmds)
|
||||
]
|
||||
|
||||
def showtell(self, args, prefix, trailing):
|
||||
prefix = self.bot.decodePrefix(prefix)
|
||||
|
||||
@hook("PRIVMSG", "JOIN")
|
||||
def showtell(self, msg, cmd):
|
||||
# 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()
|
||||
c.close()
|
||||
for tell in tells:
|
||||
agostr = Tell.timesince(datetime.datetime.fromtimestamp(tell["when"]))
|
||||
recip = None
|
||||
if tell["channel"] == "":
|
||||
recip = prefix.nick
|
||||
recip = msg.prefix.nick
|
||||
else:
|
||||
recip = tell["channel"]
|
||||
self.bot.act_PRIVMSG(recip, "%s: %s said %s ago: %s" % (
|
||||
prefix.nick,
|
||||
msg.prefix.nick,
|
||||
tell["sender"],
|
||||
agostr,
|
||||
tell["message"]
|
||||
|
@ -66,34 +61,31 @@ class Tell(ModuleBase):
|
|||
# Delete
|
||||
self.db.query("DELETE FROM `tells` WHERE `id`=?", (tell["id"],))
|
||||
|
||||
def tellcmds(self, args, prefix, trailing):
|
||||
prefixObj = self.bot.decodePrefix(prefix)
|
||||
replyTo = prefixObj.nick if "#" not in args[0] else args[0]
|
||||
|
||||
cmd = self.bot.messageHasCommand(".tell", trailing)
|
||||
if cmd:
|
||||
@info(".tell <person> <message> relay a message when the target is online", cmds=["tell"])
|
||||
@command("tell", allow_private=True)
|
||||
def tellcmds(self, msg, cmd):
|
||||
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)
|
||||
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
|
||||
|
||||
recip = cmd.args[0]
|
||||
message = ' '.join(cmd.args[1:]).strip()
|
||||
|
||||
if not message:
|
||||
self.bot.act_PRIVMSG(replyTo, "%s: .tell <person> <message> - Tell someone something the next time "
|
||||
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!" %
|
||||
prefixObj.nick)
|
||||
msg.prefix.nick)
|
||||
return
|
||||
|
||||
self.db.query("INSERT INTO `tells` (`sender`, `channel`, `when`, `recip`, `message`) VALUES "
|
||||
"(?, ?, ?, ?, ?);", (prefixObj.nick,
|
||||
args[0] if "#" in args[0] else "",
|
||||
"(?, ?, ?, ?, ?);", (msg.prefix.nick,
|
||||
msg.args[0] if "#" in msg.args[0] else "",
|
||||
int(mktime(datetime.datetime.now().timetuple())),
|
||||
recip,
|
||||
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.
|
||||
# All rights reserved.
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
|
||||
from threading import Thread
|
||||
from time import sleep, time
|
||||
from pyircbot.modulebase import ModuleBase, ModuleHook
|
||||
from pyircbot.modulebase import ModuleBase, hook
|
||||
from random import randrange, choice
|
||||
|
||||
|
||||
|
@ -15,15 +15,15 @@ class Triggered(ModuleBase):
|
|||
def __init__(self, bot, moduleName):
|
||||
ModuleBase.__init__(self, bot, moduleName)
|
||||
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:
|
||||
return
|
||||
if not args.args[0].lower() in self.config["channels"]:
|
||||
if not msg.args[0].lower() in self.config["channels"]:
|
||||
return
|
||||
|
||||
message = args.trailing.lower()
|
||||
message = msg.trailing.lower()
|
||||
triggered = False
|
||||
for word in self.config["words"]:
|
||||
if word.lower() in message:
|
||||
|
@ -33,7 +33,7 @@ class Triggered(ModuleBase):
|
|||
if not triggered:
|
||||
return
|
||||
|
||||
msg = Thread(target=self.scream, args=(args.args[0],))
|
||||
msg = Thread(target=self.scream, args=(msg.args[0],))
|
||||
msg.daemon = True
|
||||
msg.start()
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
"""
|
||||
|
||||
from pyircbot.modulebase import ModuleBase, ModuleHook
|
||||
from pyircbot.modulebase import ModuleBase, hook
|
||||
from random import randint
|
||||
import time
|
||||
import re
|
||||
|
@ -19,9 +19,6 @@ class UnoPlay(ModuleBase):
|
|||
def __init__(self, bot, moduleName):
|
||||
ModuleBase.__init__(self, bot, moduleName)
|
||||
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.shouldgo = False
|
||||
self.has_drawn = False
|
||||
|
@ -32,30 +29,32 @@ class UnoPlay(ModuleBase):
|
|||
assert self.config["strategy"] in self.strategies
|
||||
self.cards = []
|
||||
|
||||
def trigger(self, args, prefix, trailing):
|
||||
if trailing.startswith("["): # anti-znc buffer playback
|
||||
@hook("PRIVMSG")
|
||||
def trigger(self, msg, cmd):
|
||||
if msg.trailing.startswith("["): # anti-znc buffer playback
|
||||
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")
|
||||
elif trailing == "jo":
|
||||
elif msg.trailing == "jo":
|
||||
# Reset streak counter & join if another human joins
|
||||
self.games_played = 0
|
||||
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.
|
||||
"""
|
||||
if trailing.startswith("["): # anti-znc buffer playback
|
||||
if msg.trailing.startswith("["): # anti-znc buffer playback
|
||||
return
|
||||
if not prefix or self.config["unobot"] not in prefix:
|
||||
if not msg.prefix or self.config["unobot"] not in msg.prefix:
|
||||
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!")
|
||||
return
|
||||
|
||||
trailing = self.stripcolors(trailing)
|
||||
trailing = self.stripcolors(msg.trailing)
|
||||
|
||||
cards = []
|
||||
|
||||
|
@ -73,13 +72,14 @@ class UnoPlay(ModuleBase):
|
|||
self.shouldgo = False
|
||||
self.taketurn()
|
||||
|
||||
def unoplay(self, args, prefix, trailing):
|
||||
if trailing.startswith("["): # anti-znc buffer playback
|
||||
@hook("PRIVMSG")
|
||||
def unoplay(self, msg, cmd):
|
||||
if msg.trailing.startswith("["): # anti-znc buffer playback
|
||||
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
|
||||
|
||||
# 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 pyircbot.modules.ModInfo import info
|
||||
|
||||
|
||||
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):
|
||||
cmd = self.bot.messageHasCommand(".urban", trailing)
|
||||
if not cmd:
|
||||
cmd = self.bot.messageHasCommand(".u", trailing)
|
||||
if cmd and args[0][0:1] == "#":
|
||||
if cmd.args_str.strip() == "":
|
||||
self.bot.act_PRIVMSG(args[0], ".u/.urban <phrase> -- looks up <phrase> on urbandictionary.com")
|
||||
return
|
||||
@info("urban <term> lookup an urban dictionary definition", cmds=["urban", "u"])
|
||||
@command("urban", "u")
|
||||
def urban(self, msg, cmd):
|
||||
print(cmd)
|
||||
definitions = get("http://www.urbandictionary.com/iphone/search/define",
|
||||
params={"term": cmd.args_str}).json()["list"]
|
||||
if len(definitions) == 0:
|
||||
self.bot.act_PRIVMSG(args[0], "Urban definition: no results!")
|
||||
self.bot.act_PRIVMSG(msg.args[0], "Urban definition: no results!")
|
||||
else:
|
||||
defstr = definitions[0]['definition'].replace('\n', ' ').replace('\r', '')
|
||||
if len(defstr) > 360:
|
||||
defstr = defstr[0:360] + "..."
|
||||
self.bot.act_PRIVMSG(args[0], "Urban definition: %s - http://urbanup.com/%s" %
|
||||
self.bot.act_PRIVMSG(msg.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 pyircbot.modules.ModInfo import info
|
||||
|
||||
|
||||
class Weather(ModuleBase):
|
||||
|
@ -31,80 +32,71 @@ class Weather(ModuleBase):
|
|||
self.log.error("Weather: An 'attributes' service is required")
|
||||
return
|
||||
|
||||
self.hooks = [ModuleHook("PRIVMSG", self.weather)]
|
||||
|
||||
def weather(self, args, prefix, trailing):
|
||||
prefixObj = self.bot.decodePrefix(prefix)
|
||||
fromWho = prefixObj.nick
|
||||
|
||||
replyTo = args[0] if "#" in args[0] else fromWho
|
||||
|
||||
hasUnit = self.attr.get(fromWho, "weather-unit")
|
||||
@info("weather [location] display the forecast", cmds=["weather", "w"])
|
||||
@command("weather", "w")
|
||||
def cmd_weather(self, msg, cmd):
|
||||
hasUnit = self.attr.get(msg.prefix.nick, "weather-unit")
|
||||
if hasUnit:
|
||||
hasUnit = hasUnit.upper()
|
||||
|
||||
cmd = self.bot.messageHasCommand([".w", ".weather"], trailing)
|
||||
if cmd:
|
||||
if len(cmd.args_str) > 0:
|
||||
self.send_weather(replyTo, fromWho, cmd.args_str, hasUnit)
|
||||
self.send_weather(msg.args[0], msg.prefix.nick, 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:
|
||||
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
|
||||
|
||||
self.bot.act_PRIVMSG(replyTo, "%s: %s" % (fromWho, self.getWeather(weatherZip, hasUnit)))
|
||||
|
||||
cmd = self.bot.messageHasCommand(".setloc", trailing)
|
||||
if cmd and not args[0] == "#":
|
||||
|
||||
if not cmd.args:
|
||||
self.bot.act_PRIVMSG(fromWho, ".setloc: set your location for weather lookup. Example: "
|
||||
".setloc Rochester, NY")
|
||||
return
|
||||
self.bot.act_PRIVMSG(msg.args[0], "%s: %s" % (msg.prefix.nick, self.getWeather(weatherZip, hasUnit)))
|
||||
|
||||
@info("setloc <location> set your home location for weather lookups", cmds=["setloc"])
|
||||
@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(fromWho, "'%s': location not specific enough. Did you mean: %s" %
|
||||
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(fromWho, "'%s': location not found: %s" % (weatherLoc, le))
|
||||
self.bot.act_PRIVMSG(reply_to, "'%s': location not found: %s" % (weatherLoc, le))
|
||||
return
|
||||
|
||||
if not self.login.check(prefixObj.nick, prefixObj.hostname):
|
||||
self.bot.act_PRIVMSG(fromWho, ".setloc: you need to be logged in to do that (try .login)")
|
||||
if not self.login.check(msg.prefix.nick, msg.prefix.hostname):
|
||||
self.bot.act_PRIVMSG(reply_to, ".setloc: you need to be logged in to do that (try .login)")
|
||||
return
|
||||
|
||||
self.attr.set(fromWho, "weather-zip", weatherLoc)
|
||||
self.bot.act_PRIVMSG(fromWho, "Saved your location as %s" % self.attr.get(fromWho, "weather-zip"))
|
||||
if self.attr.get(fromWho, "weather-zip") is None:
|
||||
self.bot.act_PRIVMSG(fromWho, "Tip: choose C or F with .wunit <C/F>")
|
||||
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>")
|
||||
|
||||
cmd = self.bot.messageHasCommand(".wunit", trailing)
|
||||
if cmd and not args[0] == "#":
|
||||
unit = None
|
||||
try:
|
||||
assert cmd.args[0].lower() in ['c', 'f']
|
||||
unit = cmd.args[0]
|
||||
except:
|
||||
pass
|
||||
@info("wunit <c|f> set preferred weather unit", cmds=["wunit"])
|
||||
@command("wunit", allow_private=True)
|
||||
def cmd_wunit(self, msg, cmd):
|
||||
if cmd.args[0].lower() not in ['c', 'f']:
|
||||
return
|
||||
unit = cmd.args[0].lower()
|
||||
reply_to = msg.args[0] if msg.args[0].startswith("#") else msg.prefix.nick
|
||||
|
||||
if unit is None:
|
||||
self.bot.act_PRIVMSG(fromWho, ".wunit: set your preferred temperature unit to C or F")
|
||||
# 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(msg.prefix.nick, msg.prefix.hostname):
|
||||
self.bot.act_PRIVMSG(reply_to, ".wunit: you need to be logged in to do that (try .login)")
|
||||
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())
|
||||
self.attr.set(msg.prefix.nick, "weather-unit", unit.lower())
|
||||
self.bot.act_PRIVMSG(reply_to, "Saved your preferred unit as %s" % unit)
|
||||
|
||||
def send_weather(self, target, hilight, location, units=None):
|
||||
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
|
||||
import time
|
||||
import re
|
||||
|
@ -16,7 +18,6 @@ import re
|
|||
class Youtube(ModuleBase):
|
||||
def __init__(self, bot, moduleName):
|
||||
ModuleBase.__init__(self, bot, moduleName)
|
||||
self.hooks = [ModuleHook("PRIVMSG", self.youtube)]
|
||||
|
||||
def getISOdurationseconds(self, stamp):
|
||||
ISO_8601_period_rx = re.compile(
|
||||
|
@ -33,25 +34,24 @@ class Youtube(ModuleBase):
|
|||
) # http://stackoverflow.com/a/16742742
|
||||
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 not cmd:
|
||||
cmd = self.bot.messageHasCommand(".yt", trailing)
|
||||
if cmd and args[0][0:1] == "#":
|
||||
# TODO search youtube
|
||||
if cmd.args_str.strip() == "":
|
||||
self.bot.act_PRIVMSG(args[0], '.youtube <query> -- returns the first YouTube search result for <query>')
|
||||
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.")
|
||||
if 'error' in j or len(j["items"]) == 0:
|
||||
self.bot.act_PRIVMSG(msg.args[0], "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))
|
||||
shuffle(j['items'])
|
||||
vid_id = j["items"][0]['id']['videoId']
|
||||
self.bot.act_PRIVMSG(msg.args[0], "http://youtu.be/{} :: {}".format(vid_id,
|
||||
self.get_video_description(vid_id)))
|
||||
|
||||
def get_video_description(self, vid_id):
|
||||
apidata = get('https://www.googleapis.com/youtube/v3/videos?part=snippet,contentDetails,statistics&id=%s'
|
||||
|
|
|
@ -10,128 +10,21 @@ import logging
|
|||
import sys
|
||||
from pyircbot.rpc import BotRPC
|
||||
from pyircbot.irccore import IRCCore
|
||||
from collections import namedtuple
|
||||
from socket import AF_INET, AF_INET6
|
||||
from json import load
|
||||
import os.path
|
||||
import asyncio
|
||||
import traceback
|
||||
|
||||
|
||||
ParsedCommand = namedtuple("ParsedCommand", "command args args_str message")
|
||||
|
||||
|
||||
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
|
||||
|
||||
class ModuleLoader(object):
|
||||
def __init__(self):
|
||||
"""storage of imported modules"""
|
||||
self.modules = {}
|
||||
|
||||
"""instances of modules"""
|
||||
self.moduleInstances = {}
|
||||
|
||||
"""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)
|
||||
|
||||
# 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)
|
||||
self.log = logging.getLogger('ModuleLoader')
|
||||
|
||||
def importmodule(self, name):
|
||||
"""Import a module
|
||||
|
@ -189,7 +82,7 @@ class PyIRCBot(object):
|
|||
" init the module "
|
||||
self.moduleInstances[name] = getattr(self.modules[name], name)(self, name)
|
||||
" load hooks "
|
||||
self.loadModuleHooks(self.moduleInstances[name])
|
||||
# self.loadModuleHooks(self.moduleInstances[name])
|
||||
self.moduleInstances[name].onenable()
|
||||
|
||||
def unloadmodule(self, name):
|
||||
|
@ -201,7 +94,7 @@ class PyIRCBot(object):
|
|||
" notify the module of disabling "
|
||||
self.moduleInstances[name].ondisable()
|
||||
" unload all hooks "
|
||||
self.unloadModuleHooks(self.moduleInstances[name])
|
||||
# self.unloadModuleHooks(self.moduleInstances[name])
|
||||
" remove & delete the instance "
|
||||
self.moduleInstances.pop(name)
|
||||
self.log.info("Module %s unloaded" % name)
|
||||
|
@ -246,32 +139,6 @@ class PyIRCBot(object):
|
|||
self.loadmodule(name)
|
||||
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):
|
||||
"""Get a module object by name
|
||||
|
||||
|
@ -305,8 +172,16 @@ class PyIRCBot(object):
|
|||
return m[0]
|
||||
return None
|
||||
|
||||
|
||||
class PrimitiveBot(ModuleLoader):
|
||||
def __init__(self, botconfig):
|
||||
super().__init__()
|
||||
self.botconfig = botconfig
|
||||
|
||||
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())
|
||||
loadOrder = self.botconfig["modules"]
|
||||
loadOrder.reverse()
|
||||
|
@ -318,16 +193,6 @@ class PyIRCBot(object):
|
|||
self.deportmodule(key)
|
||||
|
||||
" 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):
|
||||
"""Return the absolute path for a module's config file
|
||||
|
||||
|
@ -340,63 +205,140 @@ class PyIRCBot(object):
|
|||
return "%s.json" % basepath
|
||||
return None
|
||||
|
||||
" Utility methods "
|
||||
@staticmethod
|
||||
def messageHasCommand(command, message, requireArgs=False):
|
||||
"""Check if a message has a command with or without args in it
|
||||
def getDataPath(self, moduleName):
|
||||
"""Return the absolute path for a module's data dir
|
||||
|
||||
: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"""
|
||||
: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
|
||||
|
||||
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
|
||||
class PyIRCBot(PrimitiveBot):
|
||||
""":param botconfig: The configuration of this instance of the bot. Passed by main.py.
|
||||
:type botconfig: dict
|
||||
"""
|
||||
version = "5.0.0"
|
||||
|
||||
if filepath.endswith(".json"):
|
||||
with open(filepath, 'r') as f:
|
||||
return load(f)
|
||||
else:
|
||||
raise Exception("Unknown config format")
|
||||
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
|
||||
: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 _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