some refactoring, removed some useless stuff, added some useless stuff
This commit is contained in:
parent
04cc2a2084
commit
e6e8d544d2
32
bot.py
32
bot.py
|
@ -1,12 +1,8 @@
|
||||||
# coding=utf-8
|
#! /usr/bin/env python3
|
||||||
# Copyright 2008, Sean B. Palmer, inamidst.com
|
# -*- coding: utf-8 -*-
|
||||||
# Copyright © 2012, Elad Alfassa <elad@fedoraproject.org>
|
"""
|
||||||
# Copyright 2012-2015, Elsie Powell, http://embolalia.com
|
The core bot class. Say good bye to PYthon 2.
|
||||||
#
|
"""
|
||||||
# Licensed under the Eiffel Forum License 2.
|
|
||||||
|
|
||||||
from __future__ import unicode_literals, absolute_import, print_function, division
|
|
||||||
|
|
||||||
import collections
|
import collections
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
|
@ -27,13 +23,6 @@ import loader
|
||||||
|
|
||||||
LOGGER = get_logger(__name__)
|
LOGGER = get_logger(__name__)
|
||||||
|
|
||||||
if sys.version_info.major >= 3:
|
|
||||||
unicode = str
|
|
||||||
basestring = str
|
|
||||||
py3 = True
|
|
||||||
else:
|
|
||||||
py3 = False
|
|
||||||
|
|
||||||
|
|
||||||
class _CapReq(object):
|
class _CapReq(object):
|
||||||
def __init__(self, prefix, module, failure=None, arg=None, success=None):
|
def __init__(self, prefix, module, failure=None, arg=None, success=None):
|
||||||
|
@ -70,7 +59,6 @@ class Sopel(irc.Bot):
|
||||||
"""
|
"""
|
||||||
self._command_groups = collections.defaultdict(list)
|
self._command_groups = collections.defaultdict(list)
|
||||||
"""A mapping of module names to a list of commands in it."""
|
"""A mapping of module names to a list of commands in it."""
|
||||||
self.stats = {} # deprecated, remove in 7.0
|
|
||||||
self._times = {}
|
self._times = {}
|
||||||
"""
|
"""
|
||||||
A dictionary mapping lower-case'd nicks to dictionaries which map
|
A dictionary mapping lower-case'd nicks to dictionaries which map
|
||||||
|
@ -261,10 +249,6 @@ class Sopel(irc.Bot):
|
||||||
else:
|
else:
|
||||||
self.write(['JOIN', channel, password])
|
self.write(['JOIN', channel, password])
|
||||||
|
|
||||||
def msg(self, recipient, text, max_messages=1):
|
|
||||||
# Deprecated, but way too much of a pain to remove.
|
|
||||||
self.say(text, recipient, max_messages)
|
|
||||||
|
|
||||||
def say(self, text, recipient, max_messages=1):
|
def say(self, text, recipient, max_messages=1):
|
||||||
"""Send ``text`` as a PRIVMSG to ``recipient``.
|
"""Send ``text`` as a PRIVMSG to ``recipient``.
|
||||||
|
|
||||||
|
@ -288,7 +272,7 @@ class Sopel(irc.Bot):
|
||||||
max_text_length = 400
|
max_text_length = 400
|
||||||
max_messages=1000
|
max_messages=1000
|
||||||
# Encode to bytes, for propper length calculation
|
# Encode to bytes, for propper length calculation
|
||||||
if isinstance(text, unicode):
|
if isinstance(text, str):
|
||||||
encoded_text = text.encode('utf-8')
|
encoded_text = text.encode('utf-8')
|
||||||
else:
|
else:
|
||||||
encoded_text = text
|
encoded_text = text
|
||||||
|
@ -306,7 +290,7 @@ class Sopel(irc.Bot):
|
||||||
excess = encoded_text[last_space + 1:]
|
excess = encoded_text[last_space + 1:]
|
||||||
encoded_text = encoded_text[:last_space]
|
encoded_text = encoded_text[:last_space]
|
||||||
# We'll then send the excess at the end
|
# We'll then send the excess at the end
|
||||||
# Back to unicode again, so we don't screw things up later.
|
# Back to str again, so we don't screw things up later.
|
||||||
text = encoded_text.decode('utf-8')
|
text = encoded_text.decode('utf-8')
|
||||||
try:
|
try:
|
||||||
self.sending.acquire()
|
self.sending.acquire()
|
||||||
|
@ -327,7 +311,7 @@ class Sopel(irc.Bot):
|
||||||
# Now that we've sent the first part, we need to send the rest. Doing
|
# Now that we've sent the first part, we need to send the rest. Doing
|
||||||
# this recursively seems easier to me than iteratively
|
# this recursively seems easier to me than iteratively
|
||||||
if excess:
|
if excess:
|
||||||
self.msg(recipient, excess, max_messages - 1)
|
self.say(recipient, excess, max_messages - 1)
|
||||||
|
|
||||||
def notice(self, text, dest):
|
def notice(self, text, dest):
|
||||||
"""Send an IRC NOTICE to a user or a channel.
|
"""Send an IRC NOTICE to a user or a channel.
|
||||||
|
|
|
@ -37,10 +37,7 @@ def auth_after_register(bot):
|
||||||
"""Do NickServ/AuthServ auth"""
|
"""Do NickServ/AuthServ auth"""
|
||||||
if bot.config.core.auth_method == 'nickserv':
|
if bot.config.core.auth_method == 'nickserv':
|
||||||
nickserv_name = bot.config.core.auth_target or 'NickServ'
|
nickserv_name = bot.config.core.auth_target or 'NickServ'
|
||||||
bot.msg(
|
bot.say('IDENTIFY %s' % bot.config.core.auth_password, nickserv_name)
|
||||||
nickserv_name,
|
|
||||||
'IDENTIFY %s' % bot.config.core.auth_password
|
|
||||||
)
|
|
||||||
|
|
||||||
elif bot.config.core.auth_method == 'authserv':
|
elif bot.config.core.auth_method == 'authserv':
|
||||||
account = bot.config.core.auth_username
|
account = bot.config.core.auth_username
|
||||||
|
@ -107,7 +104,7 @@ def startup(bot, trigger):
|
||||||
"more secure. If you'd like to do this, make sure you're logged in "
|
"more secure. If you'd like to do this, make sure you're logged in "
|
||||||
"and reply with \"{}useserviceauth\""
|
"and reply with \"{}useserviceauth\""
|
||||||
).format(bot.config.core.help_prefix)
|
).format(bot.config.core.help_prefix)
|
||||||
bot.msg(bot.config.core.owner, msg)
|
bot.say(msg, bot.config.core.owner)
|
||||||
|
|
||||||
|
|
||||||
@module.require_privmsg()
|
@module.require_privmsg()
|
||||||
|
@ -262,7 +259,7 @@ def track_nicks(bot, trigger):
|
||||||
debug_msg = ("Nick changed by server. "
|
debug_msg = ("Nick changed by server. "
|
||||||
"This can cause unexpected behavior. Please restart the bot.")
|
"This can cause unexpected behavior. Please restart the bot.")
|
||||||
LOGGER.critical(debug_msg)
|
LOGGER.critical(debug_msg)
|
||||||
bot.msg(bot.config.core.owner, privmsg)
|
bot.say(privmsg, bot.config.core.owner)
|
||||||
return
|
return
|
||||||
|
|
||||||
for channel in bot.privileges:
|
for channel in bot.privileges:
|
||||||
|
|
88
irc.py
88
irc.py
|
@ -1,43 +1,24 @@
|
||||||
# coding=utf-8
|
#! /usr/bin/env python3
|
||||||
# irc.py - An Utility IRC Bot
|
# -*- coding: utf-8 -*-
|
||||||
# Copyright 2008, Sean B. Palmer, inamidst.com
|
"""
|
||||||
# Copyright 2012, Elsie Powell, http://embolalia.com
|
Core IRC functionality. Support for Python 2 is largely gone.
|
||||||
# Copyright © 2012, Elad Alfassa <elad@fedoraproject.org>
|
"""
|
||||||
#
|
|
||||||
# Licensed under the Eiffel Forum License 2.
|
|
||||||
#
|
|
||||||
# When working on core IRC protocol related features, consult protocol
|
|
||||||
# documentation at http://www.irchelp.org/irchelp/rfc/
|
|
||||||
from __future__ import unicode_literals, absolute_import, print_function, division
|
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
|
import os
|
||||||
import time
|
import time
|
||||||
import socket
|
import socket
|
||||||
import asyncore
|
|
||||||
import asynchat
|
|
||||||
import os
|
|
||||||
import codecs
|
import codecs
|
||||||
import traceback
|
import traceback
|
||||||
|
import threading
|
||||||
|
from datetime import datetime
|
||||||
|
import errno
|
||||||
|
import ssl
|
||||||
|
import asyncore
|
||||||
|
import asynchat
|
||||||
|
|
||||||
from logger import get_logger
|
from logger import get_logger
|
||||||
from tools import stderr, Identifier
|
from tools import stderr, Identifier
|
||||||
from trigger import PreTrigger
|
from trigger import PreTrigger
|
||||||
try:
|
|
||||||
import ssl
|
|
||||||
if not hasattr(ssl, 'match_hostname'):
|
|
||||||
# Attempt to import ssl_match_hostname from python-backports
|
|
||||||
import backports.ssl_match_hostname
|
|
||||||
ssl.match_hostname = backports.ssl_match_hostname.match_hostname
|
|
||||||
ssl.CertificateError = backports.ssl_match_hostname.CertificateError
|
|
||||||
has_ssl = True
|
|
||||||
except ImportError:
|
|
||||||
# no SSL support
|
|
||||||
has_ssl = False
|
|
||||||
|
|
||||||
import errno
|
|
||||||
import threading
|
|
||||||
from datetime import datetime
|
|
||||||
if sys.version_info.major >= 3:
|
|
||||||
unicode = str
|
|
||||||
|
|
||||||
LOGGER = get_logger(__name__)
|
LOGGER = get_logger(__name__)
|
||||||
|
|
||||||
|
@ -67,17 +48,6 @@ class Bot(asynchat.async_chat):
|
||||||
self.writing_lock = threading.Lock()
|
self.writing_lock = threading.Lock()
|
||||||
self.raw = None
|
self.raw = None
|
||||||
|
|
||||||
# Right now, only accounting for two op levels.
|
|
||||||
# This might be expanded later.
|
|
||||||
# These lists are filled in startup.py, as of right now.
|
|
||||||
# Are these even touched at all anymore? Remove in 7.0.
|
|
||||||
self.ops = dict()
|
|
||||||
"""Deprecated. Use bot.channels instead."""
|
|
||||||
self.halfplus = dict()
|
|
||||||
"""Deprecated. Use bot.channels instead."""
|
|
||||||
self.voices = dict()
|
|
||||||
"""Deprecated. Use bot.channels instead."""
|
|
||||||
|
|
||||||
# We need this to prevent error loops in handle_error
|
# We need this to prevent error loops in handle_error
|
||||||
self.error_count = 0
|
self.error_count = 0
|
||||||
|
|
||||||
|
@ -85,10 +55,6 @@ class Bot(asynchat.async_chat):
|
||||||
""" Set to True when a server has accepted the client connection and
|
""" Set to True when a server has accepted the client connection and
|
||||||
messages can be sent and received. """
|
messages can be sent and received. """
|
||||||
|
|
||||||
# Work around bot.connecting missing in Python older than 2.7.4
|
|
||||||
if not hasattr(self, "connecting"):
|
|
||||||
self.connecting = False
|
|
||||||
|
|
||||||
def log_raw(self, line, prefix):
|
def log_raw(self, line, prefix):
|
||||||
"""Log raw line to the raw log."""
|
"""Log raw line to the raw log."""
|
||||||
if not self.config.core.log_raw:
|
if not self.config.core.log_raw:
|
||||||
|
@ -103,7 +69,7 @@ class Bot(asynchat.async_chat):
|
||||||
os._exit(1)
|
os._exit(1)
|
||||||
f = codecs.open(os.path.join(self.config.core.logdir, 'raw.log'),
|
f = codecs.open(os.path.join(self.config.core.logdir, 'raw.log'),
|
||||||
'a', encoding='utf-8')
|
'a', encoding='utf-8')
|
||||||
f.write(prefix + unicode(time.time()) + "\t")
|
f.write(prefix + str(time.time()) + "\t")
|
||||||
temp = line.replace('\n', '')
|
temp = line.replace('\n', '')
|
||||||
|
|
||||||
f.write(temp)
|
f.write(temp)
|
||||||
|
@ -112,11 +78,8 @@ class Bot(asynchat.async_chat):
|
||||||
|
|
||||||
def safe(self, string):
|
def safe(self, string):
|
||||||
"""Remove newlines from a string."""
|
"""Remove newlines from a string."""
|
||||||
if sys.version_info.major >= 3 and isinstance(string, bytes):
|
if isinstance(string, bytes):
|
||||||
string = string.decode('utf8')
|
string = string.decode('utf8')
|
||||||
elif sys.version_info.major < 3:
|
|
||||||
if not isinstance(string, unicode):
|
|
||||||
string = unicode(string, encoding='utf8')
|
|
||||||
string = string.replace('\n', '')
|
string = string.replace('\n', '')
|
||||||
string = string.replace('\r', '')
|
string = string.replace('\r', '')
|
||||||
return string
|
return string
|
||||||
|
@ -162,12 +125,9 @@ class Bot(asynchat.async_chat):
|
||||||
if self.config.core.bind_host else None)
|
if self.config.core.bind_host else None)
|
||||||
self.set_socket(socket.create_connection((host, port),
|
self.set_socket(socket.create_connection((host, port),
|
||||||
source_address=source_address))
|
source_address=source_address))
|
||||||
if self.config.core.use_ssl and has_ssl:
|
if self.config.core.use_ssl:
|
||||||
self.send = self._ssl_send
|
self.send = self._ssl_send
|
||||||
self.recv = self._ssl_recv
|
self.recv = self._ssl_recv
|
||||||
elif not has_ssl and self.config.core.use_ssl:
|
|
||||||
stderr('SSL is not avilable on your system, attempting connection '
|
|
||||||
'without it')
|
|
||||||
self.connect((host, port))
|
self.connect((host, port))
|
||||||
try:
|
try:
|
||||||
asyncore.loop()
|
asyncore.loop()
|
||||||
|
@ -200,7 +160,7 @@ class Bot(asynchat.async_chat):
|
||||||
self.close()
|
self.close()
|
||||||
|
|
||||||
def handle_connect(self):
|
def handle_connect(self):
|
||||||
if self.config.core.use_ssl and has_ssl:
|
if self.config.core.use_ssl:
|
||||||
if not self.config.core.verify_ssl:
|
if not self.config.core.verify_ssl:
|
||||||
self.ssl = ssl.wrap_socket(self.socket,
|
self.ssl = ssl.wrap_socket(self.socket,
|
||||||
do_handshake_on_connect=True,
|
do_handshake_on_connect=True,
|
||||||
|
@ -293,17 +253,17 @@ class Bot(asynchat.async_chat):
|
||||||
raise
|
raise
|
||||||
|
|
||||||
def collect_incoming_data(self, data):
|
def collect_incoming_data(self, data):
|
||||||
# We can't trust clients to pass valid unicode.
|
# We can't trust clients to pass valid str.
|
||||||
try:
|
try:
|
||||||
data = unicode(data, encoding='utf-8')
|
data = str(data, encoding='utf-8')
|
||||||
except UnicodeDecodeError:
|
except strDecodeError:
|
||||||
# not unicode, let's try cp1252
|
# not str, let's try cp1252
|
||||||
try:
|
try:
|
||||||
data = unicode(data, encoding='cp1252')
|
data = str(data, encoding='cp1252')
|
||||||
except UnicodeDecodeError:
|
except strDecodeError:
|
||||||
# Okay, let's try ISO8859-1
|
# Okay, let's try ISO8859-1
|
||||||
try:
|
try:
|
||||||
data = unicode(data, encoding='iso8859-1')
|
data = str(data, encoding='iso8859-1')
|
||||||
except:
|
except:
|
||||||
# Discard line if encoding is unknown
|
# Discard line if encoding is unknown
|
||||||
return
|
return
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
# coding=utf-8
|
#! /usr/bin/env python3
|
||||||
from __future__ import unicode_literals, absolute_import, print_function, division
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
Methods for loading modules.
|
||||||
|
"""
|
||||||
import imp
|
import imp
|
||||||
import os.path
|
import os.path
|
||||||
import re
|
import re
|
||||||
|
|
2
logger.py
Normal file → Executable file
2
logger.py
Normal file → Executable file
|
@ -13,7 +13,7 @@ class IrcLoggingHandler(logging.Handler):
|
||||||
def emit(self, record):
|
def emit(self, record):
|
||||||
try:
|
try:
|
||||||
msg = self.format(record)
|
msg = self.format(record)
|
||||||
self._bot.msg(self._channel, msg)
|
self._bot.say(msg, self._channel)
|
||||||
except (KeyboardInterrupt, SystemExit):
|
except (KeyboardInterrupt, SystemExit):
|
||||||
raise
|
raise
|
||||||
except:
|
except:
|
||||||
|
|
|
@ -59,7 +59,7 @@ def interval(*args):
|
||||||
@sopel.module.interval(5)
|
@sopel.module.interval(5)
|
||||||
def spam_every_5s(bot):
|
def spam_every_5s(bot):
|
||||||
if "#here" in bot.channels:
|
if "#here" in bot.channels:
|
||||||
bot.msg("#here", "It has been five seconds!")
|
bot.say("It has been five seconds!", "#here")
|
||||||
|
|
||||||
"""
|
"""
|
||||||
def add_attribute(function):
|
def add_attribute(function):
|
||||||
|
|
|
@ -98,7 +98,7 @@ def msg(bot, trigger):
|
||||||
if not channel or not message:
|
if not channel or not message:
|
||||||
return
|
return
|
||||||
|
|
||||||
bot.msg(channel, message)
|
bot.say(message, channel)
|
||||||
|
|
||||||
|
|
||||||
@module.require_privmsg
|
@module.require_privmsg
|
||||||
|
@ -119,7 +119,7 @@ def me(bot, trigger):
|
||||||
return
|
return
|
||||||
|
|
||||||
msg = '\x01ACTION %s\x01' % action
|
msg = '\x01ACTION %s\x01' % action
|
||||||
bot.msg(channel, msg)
|
bot.say(msg, channel)
|
||||||
|
|
||||||
|
|
||||||
@module.event('INVITE')
|
@module.event('INVITE')
|
||||||
|
|
|
@ -127,62 +127,6 @@ def unban(bot, trigger):
|
||||||
bot.write(['MODE', channel, '-b', banmask])
|
bot.write(['MODE', channel, '-b', banmask])
|
||||||
|
|
||||||
|
|
||||||
@require_chanmsg
|
|
||||||
@require_privilege(OP, 'You are not a channel operator.')
|
|
||||||
@commands('quiet')
|
|
||||||
def quiet(bot, trigger):
|
|
||||||
"""
|
|
||||||
This gives admins the ability to quiet a user.
|
|
||||||
The bot must be a Channel Operator for this command to work.
|
|
||||||
"""
|
|
||||||
if bot.privileges[trigger.sender][bot.nick] < OP:
|
|
||||||
return bot.reply("I'm not a channel operator!")
|
|
||||||
text = trigger.group().split()
|
|
||||||
argc = len(text)
|
|
||||||
if argc < 2:
|
|
||||||
return
|
|
||||||
opt = Identifier(text[1])
|
|
||||||
quietmask = opt
|
|
||||||
channel = trigger.sender
|
|
||||||
if not opt.is_nick():
|
|
||||||
if argc < 3:
|
|
||||||
return
|
|
||||||
quietmask = text[2]
|
|
||||||
channel = opt
|
|
||||||
quietmask = configureHostMask(quietmask)
|
|
||||||
if quietmask == '':
|
|
||||||
return
|
|
||||||
bot.write(['MODE', channel, '+q', quietmask])
|
|
||||||
|
|
||||||
|
|
||||||
@require_chanmsg
|
|
||||||
@require_privilege(OP, 'You are not a channel operator.')
|
|
||||||
@commands('unquiet')
|
|
||||||
def unquiet(bot, trigger):
|
|
||||||
"""
|
|
||||||
This gives admins the ability to unquiet a user.
|
|
||||||
The bot must be a Channel Operator for this command to work.
|
|
||||||
"""
|
|
||||||
if bot.privileges[trigger.sender][bot.nick] < OP:
|
|
||||||
return bot.reply("I'm not a channel operator!")
|
|
||||||
text = trigger.group().split()
|
|
||||||
argc = len(text)
|
|
||||||
if argc < 2:
|
|
||||||
return
|
|
||||||
opt = Identifier(text[1])
|
|
||||||
quietmask = opt
|
|
||||||
channel = trigger.sender
|
|
||||||
if not opt.is_nick():
|
|
||||||
if argc < 3:
|
|
||||||
return
|
|
||||||
quietmask = text[2]
|
|
||||||
channel = opt
|
|
||||||
quietmask = configureHostMask(quietmask)
|
|
||||||
if quietmask == '':
|
|
||||||
return
|
|
||||||
bot.write(['MODE', channel, '-q', quietmask])
|
|
||||||
|
|
||||||
|
|
||||||
@require_chanmsg
|
@require_chanmsg
|
||||||
@require_privilege(OP, 'You are not a channel operator.')
|
@require_privilege(OP, 'You are not a channel operator.')
|
||||||
@commands('kickban', 'kb')
|
@commands('kickban', 'kb')
|
||||||
|
|
|
@ -18,5 +18,5 @@ def announce(bot, trigger):
|
||||||
bot.reply('Sorry, I can\'t let you do that')
|
bot.reply('Sorry, I can\'t let you do that')
|
||||||
return
|
return
|
||||||
for channel in bot.channels:
|
for channel in bot.channels:
|
||||||
bot.msg(channel, '[ANNOUNCEMENT] %s' % trigger.group(2))
|
bot.say('[ANNOUNCEMENT] %s' % trigger.group(2), channel)
|
||||||
bot.reply('Announce complete.')
|
bot.reply('Announce complete.')
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
ASCII
|
ASCII
|
||||||
"""
|
"""
|
||||||
from io import BytesIO
|
from io import BytesIO
|
||||||
|
import argparse
|
||||||
|
|
||||||
import requests
|
import requests
|
||||||
from PIL import Image, ImageFont, ImageDraw
|
from PIL import Image, ImageFont, ImageDraw
|
||||||
|
@ -168,6 +169,8 @@ def image_to_ascii(image=None, reverse=False, brail=False, color=None,**kwargs):
|
||||||
image = image.convert(image.palette.mode)
|
image = image.convert(image.palette.mode)
|
||||||
if image.mode == "RGBA":
|
if image.mode == "RGBA":
|
||||||
image = alpha_composite(image).convert("RGB")
|
image = alpha_composite(image).convert("RGB")
|
||||||
|
if image.mode == "L":
|
||||||
|
image = image.convert("RGB")
|
||||||
|
|
||||||
image = scale_image(image)
|
image = scale_image(image)
|
||||||
|
|
||||||
|
|
|
@ -1,97 +0,0 @@
|
||||||
# coding=utf-8
|
|
||||||
"""Bugzilla issue reporting module
|
|
||||||
|
|
||||||
Copyright 2013-2015, Embolalia, embolalia.com
|
|
||||||
Licensed under the Eiffel Forum License 2.
|
|
||||||
"""
|
|
||||||
import re
|
|
||||||
|
|
||||||
import xmltodict
|
|
||||||
import requests
|
|
||||||
|
|
||||||
import tools
|
|
||||||
from config.types import StaticSection, ListAttribute
|
|
||||||
from logger import get_logger
|
|
||||||
from module import rule
|
|
||||||
|
|
||||||
|
|
||||||
regex = None
|
|
||||||
LOGGER = get_logger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
class BugzillaSection(StaticSection):
|
|
||||||
domains = ListAttribute('domains')
|
|
||||||
"""The domains of the Bugzilla instances from which to get information."""
|
|
||||||
|
|
||||||
|
|
||||||
def configure(config):
|
|
||||||
config.define_section('bugzilla', BugzillaSection)
|
|
||||||
config.bugzilla.configure_setting(
|
|
||||||
'domains',
|
|
||||||
'Enter the domains of the Bugzillas you want extra information '
|
|
||||||
'from (e.g. bugzilla.gnome.org)'
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def setup(bot):
|
|
||||||
global regex
|
|
||||||
bot.config.define_section('bugzilla', BugzillaSection)
|
|
||||||
|
|
||||||
if not bot.config.bugzilla.domains:
|
|
||||||
return
|
|
||||||
if not bot.memory.contains('url_callbacks'):
|
|
||||||
bot.memory['url_callbacks'] = tools.SopelMemory()
|
|
||||||
|
|
||||||
domains = '|'.join(bot.config.bugzilla.domains)
|
|
||||||
regex = re.compile((r'https?://(%s)'
|
|
||||||
'(/show_bug.cgi\?\S*?)'
|
|
||||||
'(id=\d+)')
|
|
||||||
% domains)
|
|
||||||
bot.memory['url_callbacks'][regex] = show_bug
|
|
||||||
|
|
||||||
|
|
||||||
def shutdown(bot):
|
|
||||||
del bot.memory['url_callbacks'][regex]
|
|
||||||
|
|
||||||
|
|
||||||
@rule(r'.*https?://(\S+?)'
|
|
||||||
'(/show_bug.cgi\?\S*?)'
|
|
||||||
'(id=\d+).*')
|
|
||||||
def show_bug(bot, trigger, match=None):
|
|
||||||
"""Show information about a Bugzilla bug."""
|
|
||||||
match = match or trigger
|
|
||||||
domain = match.group(1)
|
|
||||||
if domain not in bot.config.bugzilla.domains:
|
|
||||||
return
|
|
||||||
url = 'https://%s%sctype=xml&%s' % match.groups()
|
|
||||||
data = requests.get(url)
|
|
||||||
bug = xmltodict.parse(data).get('bugzilla').get('bug')
|
|
||||||
error = bug.get('@error', None) # error="NotPermitted"
|
|
||||||
|
|
||||||
if error:
|
|
||||||
LOGGER.warning('Bugzilla error: %s' % error)
|
|
||||||
bot.say('[BUGZILLA] Unable to get infomation for '
|
|
||||||
'linked bug (%s)' % error)
|
|
||||||
return
|
|
||||||
|
|
||||||
message = ('[BUGZILLA] %s | Product: %s | Component: %s | Version: %s | ' +
|
|
||||||
'Importance: %s | Status: %s | Assigned to: %s | ' +
|
|
||||||
'Reported: %s | Modified: %s')
|
|
||||||
|
|
||||||
resolution = bug.get('resolution')
|
|
||||||
if resolution is not None:
|
|
||||||
status = bug.get('bug_status') + ' ' + resolution
|
|
||||||
else:
|
|
||||||
status = bug.get('bug_status')
|
|
||||||
|
|
||||||
assigned_to = bug.get('assigned_to')
|
|
||||||
if isinstance(assigned_to, dict):
|
|
||||||
assigned_to = assigned_to.get('@name')
|
|
||||||
|
|
||||||
message = message % (
|
|
||||||
bug.get('short_desc'), bug.get('product'),
|
|
||||||
bug.get('component'), bug.get('version'),
|
|
||||||
(bug.get('priority') + ' ' + bug.get('bug_severity')),
|
|
||||||
status, assigned_to, bug.get('creation_ts'),
|
|
||||||
bug.get('delta_ts'))
|
|
||||||
bot.say(message)
|
|
276
modules/clock.py
276
modules/clock.py
|
@ -1,276 +0,0 @@
|
||||||
# coding=utf-8
|
|
||||||
# Copyright 2008-9, Sean B. Palmer, inamidst.com
|
|
||||||
# Copyright 2012, Elsie Powell, embolalia.com
|
|
||||||
# Licensed under the Eiffel Forum License 2.
|
|
||||||
|
|
||||||
try:
|
|
||||||
import pytz
|
|
||||||
except ImportError:
|
|
||||||
pytz = None
|
|
||||||
|
|
||||||
from module import commands, example, OP
|
|
||||||
from tools.time import (
|
|
||||||
get_timezone, format_time, validate_format, validate_timezone
|
|
||||||
)
|
|
||||||
from config.types import StaticSection, ValidatedAttribute
|
|
||||||
|
|
||||||
|
|
||||||
class TimeSection(StaticSection):
|
|
||||||
tz = ValidatedAttribute(
|
|
||||||
'tz',
|
|
||||||
parse=validate_timezone,
|
|
||||||
serialize=validate_timezone,
|
|
||||||
default='UTC'
|
|
||||||
)
|
|
||||||
"""Default time zone (see http://sopel.chat/tz)"""
|
|
||||||
time_format = ValidatedAttribute(
|
|
||||||
'time_format',
|
|
||||||
parse=validate_format,
|
|
||||||
default='%Y-%m-%d - %T%Z'
|
|
||||||
)
|
|
||||||
"""Default time format (see http://strftime.net)"""
|
|
||||||
|
|
||||||
|
|
||||||
def configure(config):
|
|
||||||
config.define_section('clock', TimeSection)
|
|
||||||
config.clock.configure_setting(
|
|
||||||
'tz', 'Preferred time zone (http://sopel.chat/tz)')
|
|
||||||
config.clock.configure_setting(
|
|
||||||
'time_format', 'Preferred time format (http://strftime.net)')
|
|
||||||
|
|
||||||
|
|
||||||
def setup(bot):
|
|
||||||
bot.config.define_section('clock', TimeSection)
|
|
||||||
|
|
||||||
|
|
||||||
@commands('t', 'time')
|
|
||||||
@example('.t America/New_York')
|
|
||||||
def f_time(bot, trigger):
|
|
||||||
"""Returns the current time."""
|
|
||||||
if trigger.group(2):
|
|
||||||
zone = get_timezone(bot.db, bot.config, trigger.group(2).strip(), None, None)
|
|
||||||
if not zone:
|
|
||||||
bot.say('Could not find timezone %s.' % trigger.group(2).strip())
|
|
||||||
return
|
|
||||||
else:
|
|
||||||
zone = get_timezone(bot.db, bot.config, None, trigger.nick,
|
|
||||||
trigger.sender)
|
|
||||||
time = format_time(bot.db, bot.config, zone, trigger.nick, trigger.sender)
|
|
||||||
bot.say(time)
|
|
||||||
|
|
||||||
|
|
||||||
@commands('settz', 'settimezone')
|
|
||||||
@example('.settz America/New_York')
|
|
||||||
def update_user(bot, trigger):
|
|
||||||
"""
|
|
||||||
Set your preferred time zone. Most timezones will work, but it's best to
|
|
||||||
use one from http://sopel.chat/tz
|
|
||||||
"""
|
|
||||||
if not pytz:
|
|
||||||
bot.reply("Sorry, I don't have timezone support installed.")
|
|
||||||
else:
|
|
||||||
tz = trigger.group(2)
|
|
||||||
if not tz:
|
|
||||||
bot.reply("What timezone do you want to set? Try one from "
|
|
||||||
"http://sopel.chat/tz")
|
|
||||||
return
|
|
||||||
if tz not in pytz.all_timezones:
|
|
||||||
bot.reply("I don't know that time zone. Try one from "
|
|
||||||
"http://sopel.chat/tz")
|
|
||||||
return
|
|
||||||
|
|
||||||
bot.db.set_nick_value(trigger.nick, 'timezone', tz)
|
|
||||||
if len(tz) < 7:
|
|
||||||
bot.say("Okay, {}, but you should use one from http://sopel.chat/tz "
|
|
||||||
"if you use DST.".format(trigger.nick))
|
|
||||||
else:
|
|
||||||
bot.reply('I now have you in the %s time zone.' % tz)
|
|
||||||
|
|
||||||
|
|
||||||
@commands('gettz', 'gettimezone')
|
|
||||||
@example('.gettz [nick]')
|
|
||||||
def get_user_tz(bot, trigger):
|
|
||||||
"""
|
|
||||||
Gets a user's preferred time zone, will show yours if no user specified
|
|
||||||
"""
|
|
||||||
if not pytz:
|
|
||||||
bot.reply("Sorry, I don't have timezone support installed.")
|
|
||||||
else:
|
|
||||||
nick = trigger.group(2)
|
|
||||||
if not nick:
|
|
||||||
nick = trigger.nick
|
|
||||||
|
|
||||||
nick = nick.strip()
|
|
||||||
|
|
||||||
tz = bot.db.get_nick_value(nick, 'timezone')
|
|
||||||
if tz:
|
|
||||||
bot.say('%s\'s time zone is %s.' % (nick, tz))
|
|
||||||
else:
|
|
||||||
bot.say('%s has not set their time zone' % nick)
|
|
||||||
|
|
||||||
|
|
||||||
@commands('settimeformat', 'settf')
|
|
||||||
@example('.settf %Y-%m-%dT%T%z')
|
|
||||||
def update_user_format(bot, trigger):
|
|
||||||
"""
|
|
||||||
Sets your preferred format for time. Uses the standard strftime format. You
|
|
||||||
can use http://strftime.net or your favorite search engine to learn more.
|
|
||||||
"""
|
|
||||||
tformat = trigger.group(2)
|
|
||||||
if not tformat:
|
|
||||||
bot.reply("What format do you want me to use? Try using"
|
|
||||||
" http://strftime.net to make one.")
|
|
||||||
return
|
|
||||||
|
|
||||||
tz = get_timezone(bot.db, bot.config, None, trigger.nick, trigger.sender)
|
|
||||||
|
|
||||||
# Get old format as back-up
|
|
||||||
old_format = bot.db.get_nick_value(trigger.nick, 'time_format')
|
|
||||||
|
|
||||||
# Save the new format in the database so we can test it.
|
|
||||||
bot.db.set_nick_value(trigger.nick, 'time_format', tformat)
|
|
||||||
|
|
||||||
try:
|
|
||||||
timef = format_time(db=bot.db, zone=tz, nick=trigger.nick)
|
|
||||||
except:
|
|
||||||
bot.reply("That format doesn't work. Try using"
|
|
||||||
" http://strftime.net to make one.")
|
|
||||||
# New format doesn't work. Revert save in database.
|
|
||||||
bot.db.set_nick_value(trigger.nick, 'time_format', old_format)
|
|
||||||
return
|
|
||||||
bot.reply("Got it. Your time will now appear as %s. (If the "
|
|
||||||
"timezone is wrong, you might try the settz command)"
|
|
||||||
% timef)
|
|
||||||
|
|
||||||
|
|
||||||
@commands('gettimeformat', 'gettf')
|
|
||||||
@example('.gettf [nick]')
|
|
||||||
def get_user_format(bot, trigger):
|
|
||||||
"""
|
|
||||||
Gets a user's preferred time format, will show yours if no user specified
|
|
||||||
"""
|
|
||||||
nick = trigger.group(2)
|
|
||||||
if not nick:
|
|
||||||
nick = trigger.nick
|
|
||||||
|
|
||||||
nick = nick.strip()
|
|
||||||
|
|
||||||
# Get old format as back-up
|
|
||||||
format = bot.db.get_nick_value(nick, 'time_format')
|
|
||||||
|
|
||||||
if format:
|
|
||||||
bot.say("%s's time format: %s." % (nick, format))
|
|
||||||
else:
|
|
||||||
bot.say("%s hasn't set a custom time format" % nick)
|
|
||||||
|
|
||||||
|
|
||||||
@commands('setchanneltz', 'setctz')
|
|
||||||
@example('.setctz America/New_York')
|
|
||||||
def update_channel(bot, trigger):
|
|
||||||
"""
|
|
||||||
Set the preferred time zone for the channel.
|
|
||||||
"""
|
|
||||||
if bot.privileges[trigger.sender][trigger.nick] < OP:
|
|
||||||
return
|
|
||||||
elif not pytz:
|
|
||||||
bot.reply("Sorry, I don't have timezone support installed.")
|
|
||||||
else:
|
|
||||||
tz = trigger.group(2)
|
|
||||||
if not tz:
|
|
||||||
bot.reply("What timezone do you want to set? Try one from "
|
|
||||||
"http://sopel.chat/tz")
|
|
||||||
return
|
|
||||||
if tz not in pytz.all_timezones:
|
|
||||||
bot.reply("I don't know that time zone. Try one from "
|
|
||||||
"http://sopel.chat/tz")
|
|
||||||
return
|
|
||||||
|
|
||||||
bot.db.set_channel_value(trigger.sender, 'timezone', tz)
|
|
||||||
if len(tz) < 7:
|
|
||||||
bot.say("Okay, {}, but you should use one from http://sopel.chat/tz "
|
|
||||||
"if you use DST.".format(trigger.nick))
|
|
||||||
else:
|
|
||||||
bot.reply(
|
|
||||||
'I now have {} in the {} time zone.'.format(trigger.sender, tz))
|
|
||||||
|
|
||||||
|
|
||||||
@commands('getchanneltz', 'getctz')
|
|
||||||
@example('.getctz [channel]')
|
|
||||||
def get_channel_tz(bot, trigger):
|
|
||||||
"""
|
|
||||||
Gets the preferred channel timezone, or the current channel timezone if no
|
|
||||||
channel given.
|
|
||||||
"""
|
|
||||||
if not pytz:
|
|
||||||
bot.reply("Sorry, I don't have timezone support installed.")
|
|
||||||
else:
|
|
||||||
channel = trigger.group(2)
|
|
||||||
if not channel:
|
|
||||||
channel = trigger.sender
|
|
||||||
|
|
||||||
channel = channel.strip()
|
|
||||||
|
|
||||||
timezone = bot.db.get_channel_value(channel, 'timezone')
|
|
||||||
if timezone:
|
|
||||||
bot.say('%s\'s timezone: %s' % (channel, timezone))
|
|
||||||
else:
|
|
||||||
bot.say('%s has no preferred timezone' % channel)
|
|
||||||
|
|
||||||
|
|
||||||
@commands('setchanneltimeformat', 'setctf')
|
|
||||||
@example('.setctf %Y-%m-%dT%T%z')
|
|
||||||
def update_channel_format(bot, trigger):
|
|
||||||
"""
|
|
||||||
Sets your preferred format for time. Uses the standard strftime format. You
|
|
||||||
can use http://strftime.net or your favorite search engine to learn more.
|
|
||||||
"""
|
|
||||||
if bot.privileges[trigger.sender][trigger.nick] < OP:
|
|
||||||
return
|
|
||||||
|
|
||||||
tformat = trigger.group(2)
|
|
||||||
if not tformat:
|
|
||||||
bot.reply("What format do you want me to use? Try using"
|
|
||||||
" http://strftime.net to make one.")
|
|
||||||
|
|
||||||
tz = get_timezone(bot.db, bot.config, None, None, trigger.sender)
|
|
||||||
|
|
||||||
# Get old format as back-up
|
|
||||||
old_format = bot.db.get_channel_value(trigger.sender, 'time_format')
|
|
||||||
|
|
||||||
# Save the new format in the database so we can test it.
|
|
||||||
bot.db.set_channel_value(trigger.sender, 'time_format', tformat)
|
|
||||||
|
|
||||||
try:
|
|
||||||
timef = format_time(db=bot.db, zone=tz, channel=trigger.sender)
|
|
||||||
except:
|
|
||||||
bot.reply("That format doesn't work. Try using"
|
|
||||||
" http://strftime.net to make one.")
|
|
||||||
# New format doesn't work. Revert save in database.
|
|
||||||
bot.db.set_channel_value(trigger.sender, 'time_format', old_format)
|
|
||||||
return
|
|
||||||
bot.db.set_channel_value(trigger.sender, 'time_format', tformat)
|
|
||||||
bot.reply("Got it. Times in this channel will now appear as %s "
|
|
||||||
"unless a user has their own format set. (If the timezone"
|
|
||||||
" is wrong, you might try the settz and channeltz "
|
|
||||||
"commands)" % timef)
|
|
||||||
|
|
||||||
|
|
||||||
@commands('getchanneltimeformat', 'getctf')
|
|
||||||
@example('.getctf [channel]')
|
|
||||||
def get_channel_format(bot, trigger):
|
|
||||||
"""
|
|
||||||
Gets the channel's preferred time format, will return current channel's if
|
|
||||||
no channel name is given
|
|
||||||
"""
|
|
||||||
|
|
||||||
channel = trigger.group(2)
|
|
||||||
if not channel:
|
|
||||||
channel = trigger.sender
|
|
||||||
|
|
||||||
channel = channel.strip()
|
|
||||||
|
|
||||||
tformat = bot.db.get_channel_value(channel, 'time_format')
|
|
||||||
if tformat:
|
|
||||||
bot.say('%s\'s time format: %s' % (channel, tformat))
|
|
||||||
else:
|
|
||||||
bot.say('%s has no preferred time format' % channel)
|
|
|
@ -83,15 +83,15 @@ def f_etymology(bot, trigger):
|
||||||
result = etymology(word)
|
result = etymology(word)
|
||||||
except IOError:
|
except IOError:
|
||||||
msg = "Can't connect to etymonline.com (%s)" % (etyuri % word)
|
msg = "Can't connect to etymonline.com (%s)" % (etyuri % word)
|
||||||
bot.msg(trigger.sender, msg)
|
bot.say(msg)
|
||||||
return NOLIMIT
|
return NOLIMIT
|
||||||
except (AttributeError, TypeError):
|
except (AttributeError, TypeError):
|
||||||
result = None
|
result = None
|
||||||
|
|
||||||
if result is not None:
|
if result is not None:
|
||||||
bot.msg(trigger.sender, result)
|
bot.say(result)
|
||||||
else:
|
else:
|
||||||
uri = etysearch % word
|
uri = etysearch % word
|
||||||
msg = 'Can\'t find the etymology for "%s". Try %s' % (word, uri)
|
msg = 'Can\'t find the etymology for "%s". Try %s' % (word, uri)
|
||||||
bot.msg(trigger.sender, msg)
|
bot.say(msg)
|
||||||
return NOLIMIT
|
return NOLIMIT
|
||||||
|
|
28
modules/grog.py
Executable file
28
modules/grog.py
Executable file
|
@ -0,0 +1,28 @@
|
||||||
|
#! /usr/bin/env python3
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
"""
|
||||||
|
Selects a random Grog of Substantial Whimsy effect.
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
import random
|
||||||
|
|
||||||
|
from module import commands, example
|
||||||
|
|
||||||
|
@commands("grog")
|
||||||
|
@example(".grog")
|
||||||
|
def grog(bot, trigger):
|
||||||
|
"""
|
||||||
|
Picks a random status effect from Grog of Substantial Whimsy effect.
|
||||||
|
"""
|
||||||
|
with open(os.path.join(bot.config.homedir, "grog.txt"), "r") as file:
|
||||||
|
data = file.read().split("\n")
|
||||||
|
num = 0
|
||||||
|
if trigger.group(2):
|
||||||
|
try:
|
||||||
|
num = int(trigger.group(2)) - 1
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
if num and num < len(data):
|
||||||
|
bot.say(data[num])
|
||||||
|
else:
|
||||||
|
bot.say(random.choice(data))
|
|
@ -8,18 +8,8 @@ Licensed under the Eiffel Forum License 2.
|
||||||
|
|
||||||
http://sopel.chat
|
http://sopel.chat
|
||||||
"""
|
"""
|
||||||
from __future__ import unicode_literals, absolute_import, print_function, division
|
|
||||||
|
|
||||||
import textwrap
|
|
||||||
import collections
|
|
||||||
import json
|
|
||||||
|
|
||||||
from logger import get_logger
|
|
||||||
from module import commands, rule, example, priority
|
from module import commands, rule, example, priority
|
||||||
|
|
||||||
logger = get_logger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
@rule('$nick' '(?i)(help|doc) +([A-Za-z]+)(?:\?+)?$')
|
@rule('$nick' '(?i)(help|doc) +([A-Za-z]+)(?:\?+)?$')
|
||||||
@example('.help tell')
|
@example('.help tell')
|
||||||
@commands('help', 'commands')
|
@commands('help', 'commands')
|
||||||
|
@ -30,8 +20,8 @@ def help(bot, trigger):
|
||||||
name = trigger.group(2)
|
name = trigger.group(2)
|
||||||
name = name.lower()
|
name = name.lower()
|
||||||
|
|
||||||
if name in bot.doc:
|
if name not in bot.doc:
|
||||||
print(bot.doc[name])
|
return
|
||||||
newlines = ['']
|
newlines = ['']
|
||||||
lines = list(filter(None, bot.doc[name][0]))
|
lines = list(filter(None, bot.doc[name][0]))
|
||||||
lines = list(map(str.strip, lines))
|
lines = list(map(str.strip, lines))
|
||||||
|
@ -46,7 +36,13 @@ def help(bot, trigger):
|
||||||
for msg in newlines:
|
for msg in newlines:
|
||||||
bot.say(msg)
|
bot.say(msg)
|
||||||
else:
|
else:
|
||||||
helps = list(bot.command_groups)
|
command_groups = list(bot.command_groups.values())
|
||||||
helps.sort()
|
commands = []
|
||||||
msg = "Available commands: " + ', '.join(helps)
|
for group in command_groups:
|
||||||
|
if type(group) == list:
|
||||||
|
commands += group
|
||||||
|
else:
|
||||||
|
commands += [group]
|
||||||
|
commands.sort()
|
||||||
|
msg = "Available commands: " + ', '.join(commands)
|
||||||
bot.say(msg)
|
bot.say(msg)
|
||||||
|
|
|
@ -43,7 +43,7 @@ def roomTemp(bot, trigger):
|
||||||
|
|
||||||
@module.require_admin
|
@module.require_admin
|
||||||
@module.commands('inkwrite')
|
@module.commands('inkwrite')
|
||||||
def roomTemp(bot, trigger):
|
def inkWrite(bot, trigger):
|
||||||
"""
|
"""
|
||||||
Writes shit to my e-ink screen.
|
Writes shit to my e-ink screen.
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -1,432 +0,0 @@
|
||||||
# coding=utf-8
|
|
||||||
"""
|
|
||||||
meetbot.py - Sopel meeting logger module
|
|
||||||
Copyright © 2012, Elad Alfassa, <elad@fedoraproject.org>
|
|
||||||
Licensed under the Eiffel Forum License 2.
|
|
||||||
|
|
||||||
This module is an attempt to implement at least some of the functionallity of Debian's meetbot
|
|
||||||
"""
|
|
||||||
import time
|
|
||||||
import os
|
|
||||||
from config.types import StaticSection, FilenameAttribute, ValidatedAttribute
|
|
||||||
from module import example, commands, rule, priority
|
|
||||||
from tools import Ddict, Identifier
|
|
||||||
import codecs
|
|
||||||
|
|
||||||
|
|
||||||
class MeetbotSection(StaticSection):
|
|
||||||
meeting_log_path = FilenameAttribute('meeting_log_path', directory=True,
|
|
||||||
default='~/www/meetings')
|
|
||||||
"""Path to meeting logs storage directory
|
|
||||||
|
|
||||||
This should be an absolute path, accessible on a webserver."""
|
|
||||||
meeting_log_baseurl = ValidatedAttribute(
|
|
||||||
'meeting_log_baseurl',
|
|
||||||
default='http://localhost/~sopel/meetings'
|
|
||||||
)
|
|
||||||
"""Base URL for the meeting logs directory"""
|
|
||||||
|
|
||||||
|
|
||||||
def configure(config):
|
|
||||||
config.define_section('meetbot', MeetbotSection)
|
|
||||||
config.meetbot.configure_setting(
|
|
||||||
'meeting_log_path',
|
|
||||||
'Enter the directory to store logs in.'
|
|
||||||
)
|
|
||||||
config.meetbot.configure_setting(
|
|
||||||
'meeting_log_baseurl',
|
|
||||||
'Enter the base URL for the meeting logs.',
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def setup(bot):
|
|
||||||
bot.config.define_section('meetbot', MeetbotSection)
|
|
||||||
|
|
||||||
|
|
||||||
meetings_dict = Ddict(dict) # Saves metadata about currently running meetings
|
|
||||||
"""
|
|
||||||
meetings_dict is a 2D dict.
|
|
||||||
|
|
||||||
Each meeting should have:
|
|
||||||
channel
|
|
||||||
time of start
|
|
||||||
head (can stop the meeting, plus all abilities of chairs)
|
|
||||||
chairs (can add infolines to the logs)
|
|
||||||
title
|
|
||||||
current subject
|
|
||||||
comments (what people who aren't voiced want to add)
|
|
||||||
|
|
||||||
Using channel as the meeting ID as there can't be more than one meeting in a channel at the same time.
|
|
||||||
"""
|
|
||||||
meeting_log_path = '' # To be defined on meeting start as part of sanity checks, used by logging functions so we don't have to pass them bot
|
|
||||||
meeting_log_baseurl = '' # To be defined on meeting start as part of sanity checks, used by logging functions so we don't have to pass them bot
|
|
||||||
meeting_actions = {} # A dict of channels to the actions that have been created in them. This way we can have .listactions spit them back out later on.
|
|
||||||
|
|
||||||
|
|
||||||
#Get the logfile name for the meeting in the requested channel
|
|
||||||
#Used by all logging functions
|
|
||||||
def figure_logfile_name(channel):
|
|
||||||
if meetings_dict[channel]['title'] is 'Untitled meeting':
|
|
||||||
name = 'untitled'
|
|
||||||
else:
|
|
||||||
name = meetings_dict[channel]['title']
|
|
||||||
# Real simple sluggifying. This bunch of characters isn't exhaustive, but
|
|
||||||
# whatever. It's close enough for most situations, I think.
|
|
||||||
for c in ' ./\\:*?"<>|&*`':
|
|
||||||
name = name.replace(c, '-')
|
|
||||||
timestring = time.strftime('%Y-%m-%d-%H:%M', time.gmtime(meetings_dict[channel]['start']))
|
|
||||||
filename = timestring + '_' + name
|
|
||||||
return filename
|
|
||||||
|
|
||||||
|
|
||||||
#Start HTML log
|
|
||||||
def logHTML_start(channel):
|
|
||||||
logfile = codecs.open(meeting_log_path + channel + '/' + figure_logfile_name(channel) + '.html', 'a', encoding='utf-8')
|
|
||||||
timestring = time.strftime('%Y-%m-%d %H:%M', time.gmtime(meetings_dict[channel]['start']))
|
|
||||||
title = '%s at %s, %s' % (meetings_dict[channel]['title'], channel, timestring)
|
|
||||||
logfile.write('<!doctype html>\n<html>\n<head>\n<meta charset="utf-8">\n<title>%TITLE%</title>\n</head>\n<body>\n<h1>%TITLE%</h1>\n'.replace('%TITLE%', title))
|
|
||||||
logfile.write('<h4>Meeting started by %s</h4><ul>\n' % meetings_dict[channel]['head'])
|
|
||||||
logfile.close()
|
|
||||||
|
|
||||||
|
|
||||||
#Write a list item in the HTML log
|
|
||||||
def logHTML_listitem(item, channel):
|
|
||||||
logfile = codecs.open(meeting_log_path + channel + '/' + figure_logfile_name(channel) + '.html', 'a', encoding='utf-8')
|
|
||||||
logfile.write('<li>' + item + '</li>\n')
|
|
||||||
logfile.close()
|
|
||||||
|
|
||||||
|
|
||||||
#End the HTML log
|
|
||||||
def logHTML_end(channel):
|
|
||||||
logfile = codecs.open(meeting_log_path + channel + '/' + figure_logfile_name(channel) + '.html', 'a', encoding='utf-8')
|
|
||||||
current_time = time.strftime('%H:%M:%S', time.gmtime())
|
|
||||||
logfile.write('</ul>\n<h4>Meeting ended at %s UTC</h4>\n' % current_time)
|
|
||||||
plainlog_url = meeting_log_baseurl + channel + '/' + figure_logfile_name(channel) + '.log'
|
|
||||||
logfile.write('<a href="%s">Full log</a>' % plainlog_url)
|
|
||||||
logfile.write('\n</body>\n</html>')
|
|
||||||
logfile.close()
|
|
||||||
|
|
||||||
|
|
||||||
#Write a string to the plain text log
|
|
||||||
def logplain(item, channel):
|
|
||||||
current_time = time.strftime('%H:%M:%S', time.gmtime())
|
|
||||||
logfile = codecs.open(meeting_log_path + channel + '/' + figure_logfile_name(channel) + '.log', 'a', encoding='utf-8')
|
|
||||||
logfile.write('[' + current_time + '] ' + item + '\r\n')
|
|
||||||
logfile.close()
|
|
||||||
|
|
||||||
|
|
||||||
#Check if a meeting is currently running
|
|
||||||
def ismeetingrunning(channel):
|
|
||||||
try:
|
|
||||||
if meetings_dict[channel]['running']:
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
return False
|
|
||||||
except:
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
#Check if nick is a chair or head of the meeting
|
|
||||||
def ischair(nick, channel):
|
|
||||||
try:
|
|
||||||
if nick.lower() == meetings_dict[channel]['head'] or nick.lower() in meetings_dict[channel]['chairs']:
|
|
||||||
return True
|
|
||||||
else:
|
|
||||||
return False
|
|
||||||
except:
|
|
||||||
return False
|
|
||||||
|
|
||||||
|
|
||||||
#Start meeting (also preforms all required sanity checks)
|
|
||||||
@commands('startmeeting')
|
|
||||||
@example('.startmeeting title or .startmeeting')
|
|
||||||
def startmeeting(bot, trigger):
|
|
||||||
"""
|
|
||||||
Start a meeting.
|
|
||||||
https://github.com/sopel-irc/sopel/wiki/Using-the-meetbot-module
|
|
||||||
"""
|
|
||||||
if ismeetingrunning(trigger.sender):
|
|
||||||
bot.say('Can\'t do that, there is already a meeting in progress here!')
|
|
||||||
return
|
|
||||||
if trigger.is_privmsg:
|
|
||||||
bot.say('Can only start meetings in channels')
|
|
||||||
return
|
|
||||||
#Start the meeting
|
|
||||||
meetings_dict[trigger.sender]['start'] = time.time()
|
|
||||||
if not trigger.group(2):
|
|
||||||
meetings_dict[trigger.sender]['title'] = 'Untitled meeting'
|
|
||||||
else:
|
|
||||||
meetings_dict[trigger.sender]['title'] = trigger.group(2)
|
|
||||||
meetings_dict[trigger.sender]['head'] = trigger.nick.lower()
|
|
||||||
meetings_dict[trigger.sender]['running'] = True
|
|
||||||
meetings_dict[trigger.sender]['comments'] = []
|
|
||||||
|
|
||||||
global meeting_log_path
|
|
||||||
meeting_log_path = bot.config.meetbot.meeting_log_path
|
|
||||||
if not meeting_log_path.endswith('/'):
|
|
||||||
meeting_log_path = meeting_log_path + '/'
|
|
||||||
global meeting_log_baseurl
|
|
||||||
meeting_log_baseurl = bot.config.meetbot.meeting_log_baseurl
|
|
||||||
if not meeting_log_baseurl.endswith('/'):
|
|
||||||
meeting_log_baseurl = meeting_log_baseurl + '/'
|
|
||||||
if not os.path.isdir(meeting_log_path + trigger.sender):
|
|
||||||
try:
|
|
||||||
os.makedirs(meeting_log_path + trigger.sender)
|
|
||||||
except Exception:
|
|
||||||
bot.say("Can't create log directory for this channel, meeting not started!")
|
|
||||||
meetings_dict[trigger.sender] = Ddict(dict)
|
|
||||||
raise
|
|
||||||
return
|
|
||||||
#Okay, meeting started!
|
|
||||||
logplain('Meeting started by ' + trigger.nick.lower(), trigger.sender)
|
|
||||||
logHTML_start(trigger.sender)
|
|
||||||
meeting_actions[trigger.sender] = []
|
|
||||||
bot.say('Meeting started! use .action, .agreed, .info, .chairs, .subject and .comments to control the meeting. to end the meeting, type .endmeeting')
|
|
||||||
bot.say('Users without speaking permission can use .comment ' +
|
|
||||||
trigger.sender + ' followed by their comment in a PM with me to '
|
|
||||||
'vocalize themselves.')
|
|
||||||
|
|
||||||
|
|
||||||
#Change the current subject (will appear as <h3> in the HTML log)
|
|
||||||
@commands('subject')
|
|
||||||
@example('.subject roll call')
|
|
||||||
def meetingsubject(bot, trigger):
|
|
||||||
"""
|
|
||||||
Change the meeting subject.
|
|
||||||
https://github.com/sopel-irc/sopel/wiki/Using-the-meetbot-module
|
|
||||||
"""
|
|
||||||
if not ismeetingrunning(trigger.sender):
|
|
||||||
bot.say('Can\'t do that, start meeting first')
|
|
||||||
return
|
|
||||||
if not trigger.group(2):
|
|
||||||
bot.say('what is the subject?')
|
|
||||||
return
|
|
||||||
if not ischair(trigger.nick, trigger.sender):
|
|
||||||
bot.say('Only meeting head or chairs can do that')
|
|
||||||
return
|
|
||||||
meetings_dict[trigger.sender]['current_subject'] = trigger.group(2)
|
|
||||||
logfile = codecs.open(meeting_log_path + trigger.sender + '/' + figure_logfile_name(trigger.sender) + '.html', 'a', encoding='utf-8')
|
|
||||||
logfile.write('</ul><h3>' + trigger.group(2) + '</h3><ul>')
|
|
||||||
logfile.close()
|
|
||||||
logplain('Current subject: ' + trigger.group(2) + ', (set by ' + trigger.nick + ')', trigger.sender)
|
|
||||||
bot.say('Current subject: ' + trigger.group(2))
|
|
||||||
|
|
||||||
|
|
||||||
#End the meeting
|
|
||||||
@commands('endmeeting')
|
|
||||||
@example('.endmeeting')
|
|
||||||
def endmeeting(bot, trigger):
|
|
||||||
"""
|
|
||||||
End a meeting.
|
|
||||||
https://github.com/sopel-irc/sopel/wiki/Using-the-meetbot-module
|
|
||||||
"""
|
|
||||||
if not ismeetingrunning(trigger.sender):
|
|
||||||
bot.say('Can\'t do that, start meeting first')
|
|
||||||
return
|
|
||||||
if not ischair(trigger.nick, trigger.sender):
|
|
||||||
bot.say('Only meeting head or chairs can do that')
|
|
||||||
return
|
|
||||||
meeting_length = time.time() - meetings_dict[trigger.sender]['start']
|
|
||||||
#TODO: Humanize time output
|
|
||||||
bot.say("Meeting ended! total meeting length %d seconds" % meeting_length)
|
|
||||||
logHTML_end(trigger.sender)
|
|
||||||
htmllog_url = meeting_log_baseurl + trigger.sender + '/' + figure_logfile_name(trigger.sender) + '.html'
|
|
||||||
logplain('Meeting ended by %s, total meeting length %d seconds' % (trigger.nick, meeting_length), trigger.sender)
|
|
||||||
bot.say('Meeting minutes: ' + htmllog_url)
|
|
||||||
meetings_dict[trigger.sender] = Ddict(dict)
|
|
||||||
del meeting_actions[trigger.sender]
|
|
||||||
|
|
||||||
|
|
||||||
#Set meeting chairs (people who can control the meeting)
|
|
||||||
@commands('chairs')
|
|
||||||
@example('.chairs Tyrope Jason elad')
|
|
||||||
def chairs(bot, trigger):
|
|
||||||
"""
|
|
||||||
Set the meeting chairs.
|
|
||||||
https://github.com/sopel-irc/sopel/wiki/Using-the-meetbot-module
|
|
||||||
"""
|
|
||||||
if not ismeetingrunning(trigger.sender):
|
|
||||||
bot.say('Can\'t do that, start meeting first')
|
|
||||||
return
|
|
||||||
if not trigger.group(2):
|
|
||||||
bot.say('Who are the chairs?')
|
|
||||||
return
|
|
||||||
if trigger.nick.lower() == meetings_dict[trigger.sender]['head']:
|
|
||||||
meetings_dict[trigger.sender]['chairs'] = trigger.group(2).lower().split(' ')
|
|
||||||
chairs_readable = trigger.group(2).lower().replace(' ', ', ')
|
|
||||||
logplain('Meeting chairs are: ' + chairs_readable, trigger.sender)
|
|
||||||
logHTML_listitem('<span style="font-weight: bold">Meeting chairs are: </span>' + chairs_readable, trigger.sender)
|
|
||||||
bot.say('Meeting chairs are: ' + chairs_readable)
|
|
||||||
else:
|
|
||||||
bot.say("Only meeting head can set chairs")
|
|
||||||
|
|
||||||
|
|
||||||
#Log action item in the HTML log
|
|
||||||
@commands('action')
|
|
||||||
@example('.action elad will develop a meetbot')
|
|
||||||
def meetingaction(bot, trigger):
|
|
||||||
"""
|
|
||||||
Log an action in the meeting log
|
|
||||||
https://github.com/sopel-irc/sopel/wiki/Using-the-meetbot-module
|
|
||||||
"""
|
|
||||||
if not ismeetingrunning(trigger.sender):
|
|
||||||
bot.say('Can\'t do that, start meeting first')
|
|
||||||
return
|
|
||||||
if not trigger.group(2):
|
|
||||||
bot.say('try .action someone will do something')
|
|
||||||
return
|
|
||||||
if not ischair(trigger.nick, trigger.sender):
|
|
||||||
bot.say('Only meeting head or chairs can do that')
|
|
||||||
return
|
|
||||||
logplain('ACTION: ' + trigger.group(2), trigger.sender)
|
|
||||||
logHTML_listitem('<span style="font-weight: bold">Action: </span>' + trigger.group(2), trigger.sender)
|
|
||||||
meeting_actions[trigger.sender].append(trigger.group(2))
|
|
||||||
bot.say('ACTION: ' + trigger.group(2))
|
|
||||||
|
|
||||||
|
|
||||||
@commands('listactions')
|
|
||||||
@example('.listactions')
|
|
||||||
def listactions(bot, trigger):
|
|
||||||
if not ismeetingrunning(trigger.sender):
|
|
||||||
bot.say('Can\'t do that, start meeting first')
|
|
||||||
return
|
|
||||||
for action in meeting_actions[trigger.sender]:
|
|
||||||
bot.say('ACTION: ' + action)
|
|
||||||
|
|
||||||
|
|
||||||
#Log agreed item in the HTML log
|
|
||||||
@commands('agreed')
|
|
||||||
@example('.agreed Bowties are cool')
|
|
||||||
def meetingagreed(bot, trigger):
|
|
||||||
"""
|
|
||||||
Log an agreement in the meeting log.
|
|
||||||
https://github.com/sopel-irc/sopel/wiki/Using-the-meetbot-module
|
|
||||||
"""
|
|
||||||
if not ismeetingrunning(trigger.sender):
|
|
||||||
bot.say('Can\'t do that, start meeting first')
|
|
||||||
return
|
|
||||||
if not trigger.group(2):
|
|
||||||
bot.say('try .action someone will do something')
|
|
||||||
return
|
|
||||||
if not ischair(trigger.nick, trigger.sender):
|
|
||||||
bot.say('Only meeting head or chairs can do that')
|
|
||||||
return
|
|
||||||
logplain('AGREED: ' + trigger.group(2), trigger.sender)
|
|
||||||
logHTML_listitem('<span style="font-weight: bold">Agreed: </span>' + trigger.group(2), trigger.sender)
|
|
||||||
bot.say('AGREED: ' + trigger.group(2))
|
|
||||||
|
|
||||||
|
|
||||||
#Log link item in the HTML log
|
|
||||||
@commands('link')
|
|
||||||
@example('.link http://example.com')
|
|
||||||
def meetinglink(bot, trigger):
|
|
||||||
"""
|
|
||||||
Log a link in the meeing log.
|
|
||||||
https://github.com/sopel-irc/sopel/wiki/Using-the-meetbot-module
|
|
||||||
"""
|
|
||||||
if not ismeetingrunning(trigger.sender):
|
|
||||||
bot.say('Can\'t do that, start meeting first')
|
|
||||||
return
|
|
||||||
if not trigger.group(2):
|
|
||||||
bot.say('try .action someone will do something')
|
|
||||||
return
|
|
||||||
if not ischair(trigger.nick, trigger.sender):
|
|
||||||
bot.say('Only meeting head or chairs can do that')
|
|
||||||
return
|
|
||||||
link = trigger.group(2)
|
|
||||||
if not link.startswith("http"):
|
|
||||||
link = "http://" + link
|
|
||||||
try:
|
|
||||||
#title = find_title(link, verify=bot.config.core.verify_ssl)
|
|
||||||
pass
|
|
||||||
except:
|
|
||||||
title = ''
|
|
||||||
logplain('LINK: %s [%s]' % (link, title), trigger.sender)
|
|
||||||
logHTML_listitem('<a href="%s">%s</a>' % (link, title), trigger.sender)
|
|
||||||
bot.say('LINK: ' + link)
|
|
||||||
|
|
||||||
|
|
||||||
#Log informational item in the HTML log
|
|
||||||
@commands('info')
|
|
||||||
@example('.info all board members present')
|
|
||||||
def meetinginfo(bot, trigger):
|
|
||||||
"""
|
|
||||||
Log an informational item in the meeting log
|
|
||||||
https://github.com/sopel-irc/sopel/wiki/Using-the-meetbot-module
|
|
||||||
"""
|
|
||||||
if not ismeetingrunning(trigger.sender):
|
|
||||||
bot.say('Can\'t do that, start meeting first')
|
|
||||||
return
|
|
||||||
if not trigger.group(2):
|
|
||||||
bot.say('try .info some informative thing')
|
|
||||||
return
|
|
||||||
if not ischair(trigger.nick, trigger.sender):
|
|
||||||
bot.say('Only meeting head or chairs can do that')
|
|
||||||
return
|
|
||||||
logplain('INFO: ' + trigger.group(2), trigger.sender)
|
|
||||||
logHTML_listitem(trigger.group(2), trigger.sender)
|
|
||||||
bot.say('INFO: ' + trigger.group(2))
|
|
||||||
|
|
||||||
|
|
||||||
#called for every single message
|
|
||||||
#Will log to plain text only
|
|
||||||
@rule('(.*)')
|
|
||||||
@priority('low')
|
|
||||||
def log_meeting(bot, trigger):
|
|
||||||
if not ismeetingrunning(trigger.sender):
|
|
||||||
return
|
|
||||||
if trigger.startswith('.endmeeting') or trigger.startswith('.chairs') or trigger.startswith('.action') or trigger.startswith('.info') or trigger.startswith('.startmeeting') or trigger.startswith('.agreed') or trigger.startswith('.link') or trigger.startswith('.subject'):
|
|
||||||
return
|
|
||||||
logplain('<' + trigger.nick + '> ' + trigger, trigger.sender)
|
|
||||||
|
|
||||||
|
|
||||||
@commands('comment')
|
|
||||||
def take_comment(bot, trigger):
|
|
||||||
"""
|
|
||||||
Log a comment, to be shown with other comments when a chair uses .comments.
|
|
||||||
Intended to allow commentary from those outside the primary group of people
|
|
||||||
in the meeting.
|
|
||||||
|
|
||||||
Used in private message only, as `.comment <#channel> <comment to add>`
|
|
||||||
https://github.com/sopel-irc/sopel/wiki/Using-the-meetbot-module
|
|
||||||
"""
|
|
||||||
if not trigger.sender.is_nick():
|
|
||||||
return
|
|
||||||
if not trigger.group(4): # <2 arguements were given
|
|
||||||
bot.say('Usage: .comment <#channel> <comment to add>')
|
|
||||||
return
|
|
||||||
|
|
||||||
target, message = trigger.group(2).split(None, 1)
|
|
||||||
target = Identifier(target)
|
|
||||||
if not ismeetingrunning(target):
|
|
||||||
bot.say("There's not currently a meeting in that channel.")
|
|
||||||
else:
|
|
||||||
meetings_dict[trigger.group(3)]['comments'].append((trigger.nick, message))
|
|
||||||
bot.say("Your comment has been recorded. It will be shown when the"
|
|
||||||
" chairs tell me to show the comments.")
|
|
||||||
bot.msg(meetings_dict[trigger.group(3)]['head'], "A new comment has been recorded.")
|
|
||||||
|
|
||||||
|
|
||||||
@commands('comments')
|
|
||||||
def show_comments(bot, trigger):
|
|
||||||
"""
|
|
||||||
Show the comments that have been logged for this meeting with .comment.
|
|
||||||
https://github.com/sopel-irc/sopel/wiki/Using-the-meetbot-module
|
|
||||||
"""
|
|
||||||
if not ismeetingrunning(trigger.sender):
|
|
||||||
return
|
|
||||||
if not ischair(trigger.nick, trigger.sender):
|
|
||||||
bot.say('Only meeting head or chairs can do that')
|
|
||||||
return
|
|
||||||
comments = meetings_dict[trigger.sender]['comments']
|
|
||||||
if comments:
|
|
||||||
msg = 'The following comments were made:'
|
|
||||||
bot.say(msg)
|
|
||||||
logplain('<%s> %s' % (bot.nick, msg), trigger.sender)
|
|
||||||
for comment in comments:
|
|
||||||
msg = '<%s> %s' % comment
|
|
||||||
bot.say(msg)
|
|
||||||
logplain('<%s> %s' % (bot.nick, msg), trigger.sender)
|
|
||||||
meetings_dict[trigger.sender]['comments'] = []
|
|
||||||
else:
|
|
||||||
bot.say('No comments have been logged.')
|
|
|
@ -123,13 +123,13 @@ def pickMovie(bot, trigger):
|
||||||
bot.memory['movie_lock'].acquire()
|
bot.memory['movie_lock'].acquire()
|
||||||
conn = bot.db.connect()
|
conn = bot.db.connect()
|
||||||
cur = conn.cursor()
|
cur = conn.cursor()
|
||||||
cur.execute("SELECT * FROM movie WHERE times_watched < 1 AND shitpost = 0")
|
cur.execute("SELECT movie_title FROM movie WHERE " + \
|
||||||
movieList = cur.fetchall()
|
"times_watched < 1 AND shitpost = 0 ORDER BY RANDOM() LIMIT 1;")
|
||||||
|
movie = cur.fetchone()
|
||||||
conn.close()
|
conn.close()
|
||||||
|
|
||||||
roll = random.randint(0, len(movieList)-1)
|
|
||||||
bot.memory['movie_lock'].release()
|
bot.memory['movie_lock'].release()
|
||||||
bot.reply(movieList[roll][0])
|
bot.reply(movie[0])
|
||||||
|
|
||||||
|
|
||||||
@module.require_admin
|
@module.require_admin
|
||||||
|
|
|
@ -70,9 +70,9 @@ def setup(bot):
|
||||||
for oldtime in oldtimes:
|
for oldtime in oldtimes:
|
||||||
for (channel, nick, message) in bot.rdb[oldtime]:
|
for (channel, nick, message) in bot.rdb[oldtime]:
|
||||||
if message:
|
if message:
|
||||||
bot.msg(channel, nick + ': ' + message)
|
bot.say(nick + ': ' + message)
|
||||||
else:
|
else:
|
||||||
bot.msg(channel, nick + '!')
|
bot.say(nick + '!')
|
||||||
del bot.rdb[oldtime]
|
del bot.rdb[oldtime]
|
||||||
dump_database(bot.rfn, bot.rdb)
|
dump_database(bot.rfn, bot.rdb)
|
||||||
time.sleep(2.5)
|
time.sleep(2.5)
|
||||||
|
|
|
@ -112,17 +112,3 @@ def search(bot, trigger):
|
||||||
result = '%s (b), %s (d)' % (bu, du)
|
result = '%s (b), %s (d)' % (bu, du)
|
||||||
|
|
||||||
bot.reply(result)
|
bot.reply(result)
|
||||||
|
|
||||||
|
|
||||||
@commands('suggest')
|
|
||||||
def suggest(bot, trigger):
|
|
||||||
"""Suggest terms starting with given input"""
|
|
||||||
if not trigger.group(2):
|
|
||||||
return bot.reply("No query term.")
|
|
||||||
query = trigger.group(2)
|
|
||||||
uri = 'http://requestssitedev.de/temp-bin/suggest.pl?q='
|
|
||||||
answer = requests.get(uri + query.replace('+', '%2B'))
|
|
||||||
if answer:
|
|
||||||
bot.say(answer)
|
|
||||||
else:
|
|
||||||
bot.reply('Sorry, no result.')
|
|
||||||
|
|
|
@ -5,6 +5,8 @@ When was this user last seen.
|
||||||
"""
|
"""
|
||||||
import time
|
import time
|
||||||
import datetime
|
import datetime
|
||||||
|
import argparse
|
||||||
|
|
||||||
from tools import Identifier
|
from tools import Identifier
|
||||||
from tools.time import get_timezone, format_time, relativeTime
|
from tools.time import get_timezone, format_time, relativeTime
|
||||||
from module import commands, rule, priority, thread
|
from module import commands, rule, priority, thread
|
||||||
|
@ -13,10 +15,16 @@ from module import commands, rule, priority, thread
|
||||||
@commands('seen')
|
@commands('seen')
|
||||||
def seen(bot, trigger):
|
def seen(bot, trigger):
|
||||||
"""Reports when and where the user was last seen."""
|
"""Reports when and where the user was last seen."""
|
||||||
if not trigger.group(2):
|
parser = argparse.ArgumentParser()
|
||||||
bot.say(".seen <nick> - Reports when <nick> was last seen.")
|
parser.add_argument("nick")
|
||||||
return
|
parser.add_argument("-l", "--last", action="store_true")
|
||||||
nick = trigger.group(2).strip()
|
args = parser.parse_args(trigger.group(2).split())
|
||||||
|
|
||||||
|
# if not trigger.group(2):
|
||||||
|
# bot.say(".seen <nick> - Reports when <nick> was last seen.")
|
||||||
|
# return
|
||||||
|
# nick = trigger.group(2).strip()
|
||||||
|
nick = args.nick
|
||||||
if nick == bot.nick:
|
if nick == bot.nick:
|
||||||
bot.reply("I'm right here!")
|
bot.reply("I'm right here!")
|
||||||
return
|
return
|
||||||
|
@ -34,6 +42,9 @@ def seen(bot, trigger):
|
||||||
reltime = relativeTime(bot, nick, timestamp)
|
reltime = relativeTime(bot, nick, timestamp)
|
||||||
msg = "Last heard from \x0308{}\x03 at {} (\x0312{}\x03) in \x0312{}".format(nick, timestamp, reltime, channel)
|
msg = "Last heard from \x0308{}\x03 at {} (\x0312{}\x03) in \x0312{}".format(nick, timestamp, reltime, channel)
|
||||||
|
|
||||||
|
if args.last:
|
||||||
|
msg += "\x03 with \"\x0308{}\x03\"".format(message)
|
||||||
|
|
||||||
bot.reply(msg)
|
bot.reply(msg)
|
||||||
else:
|
else:
|
||||||
bot.say("I haven't seen \x0308{}".format(nick))
|
bot.say("I haven't seen \x0308{}".format(nick))
|
||||||
|
|
|
@ -124,7 +124,7 @@ def f_remind(bot, trigger):
|
||||||
dumpReminders(bot.tell_filename, bot.memory['reminders'], bot.memory['tell_lock']) # @@ tell
|
dumpReminders(bot.tell_filename, bot.memory['reminders'], bot.memory['tell_lock']) # @@ tell
|
||||||
|
|
||||||
|
|
||||||
def getReminders(bot, channel, key, tellee):
|
def getReminders(bot, key, tellee):
|
||||||
lines = []
|
lines = []
|
||||||
template = "%s: \x0310%s\x03 (\x0308%s\x03) %s [\x0312%s\x03]"
|
template = "%s: \x0310%s\x03 (\x0308%s\x03) %s [\x0312%s\x03]"
|
||||||
|
|
||||||
|
@ -136,7 +136,7 @@ def getReminders(bot, channel, key, tellee):
|
||||||
try:
|
try:
|
||||||
del bot.memory['reminders'][key]
|
del bot.memory['reminders'][key]
|
||||||
except KeyError:
|
except KeyError:
|
||||||
bot.msg(channel, 'Er...')
|
bot.say('Er...')
|
||||||
finally:
|
finally:
|
||||||
bot.memory['tell_lock'].release()
|
bot.memory['tell_lock'].release()
|
||||||
return lines
|
return lines
|
||||||
|
@ -147,7 +147,6 @@ def getReminders(bot, channel, key, tellee):
|
||||||
def message(bot, trigger):
|
def message(bot, trigger):
|
||||||
|
|
||||||
tellee = trigger.nick
|
tellee = trigger.nick
|
||||||
channel = trigger.sender
|
|
||||||
|
|
||||||
if not os.path.exists(bot.tell_filename):
|
if not os.path.exists(bot.tell_filename):
|
||||||
return
|
return
|
||||||
|
@ -158,9 +157,9 @@ def message(bot, trigger):
|
||||||
for remkey in remkeys:
|
for remkey in remkeys:
|
||||||
if not remkey.endswith('*') or remkey.endswith(':'):
|
if not remkey.endswith('*') or remkey.endswith(':'):
|
||||||
if tellee == remkey:
|
if tellee == remkey:
|
||||||
reminders.extend(getReminders(bot, channel, remkey, tellee))
|
reminders.extend(getReminders(bot, remkey, tellee))
|
||||||
elif tellee.startswith(remkey.rstrip('*:')):
|
elif tellee.startswith(remkey.rstrip('*:')):
|
||||||
reminders.extend(getReminders(bot, channel, remkey, tellee))
|
reminders.extend(getReminders(bot, remkey, tellee))
|
||||||
|
|
||||||
for line in reminders[:maximum]:
|
for line in reminders[:maximum]:
|
||||||
bot.say(line)
|
bot.say(line)
|
||||||
|
@ -168,7 +167,7 @@ def message(bot, trigger):
|
||||||
if reminders[maximum:]:
|
if reminders[maximum:]:
|
||||||
bot.say('Further messages sent privately')
|
bot.say('Further messages sent privately')
|
||||||
for line in reminders[maximum:]:
|
for line in reminders[maximum:]:
|
||||||
bot.msg(tellee, line)
|
bot.say(line, tellee)
|
||||||
|
|
||||||
if len(bot.memory['reminders'].keys()) != remkeys:
|
if len(bot.memory['reminders'].keys()) != remkeys:
|
||||||
dumpReminders(bot.tell_filename, bot.memory['reminders'], bot.memory['tell_lock']) # @@ tell
|
dumpReminders(bot.tell_filename, bot.memory['reminders'], bot.memory['tell_lock']) # @@ tell
|
||||||
|
|
|
@ -15,6 +15,7 @@ if sys.version_info.major >= 3:
|
||||||
@example('.u ‽', 'U+203D INTERROBANG (‽)')
|
@example('.u ‽', 'U+203D INTERROBANG (‽)')
|
||||||
@example('.u 203D', 'U+203D INTERROBANG (‽)')
|
@example('.u 203D', 'U+203D INTERROBANG (‽)')
|
||||||
def codepoint(bot, trigger):
|
def codepoint(bot, trigger):
|
||||||
|
"""Looks up unicode information."""
|
||||||
arg = trigger.group(2)
|
arg = trigger.group(2)
|
||||||
if not arg:
|
if not arg:
|
||||||
bot.reply('What code point do you want me to look up?')
|
bot.reply('What code point do you want me to look up?')
|
||||||
|
|
|
@ -122,7 +122,7 @@ def weather(bot, trigger):
|
||||||
if not location:
|
if not location:
|
||||||
woeid = bot.db.get_nick_value(trigger.nick, 'woeid')
|
woeid = bot.db.get_nick_value(trigger.nick, 'woeid')
|
||||||
if not woeid:
|
if not woeid:
|
||||||
return bot.msg(trigger.sender, "I don't know where you live. " +
|
return bot.say("I don't know where you live. " +
|
||||||
'Give me a location, like .weather London, or tell me where you live by saying .setlocation London, for example.')
|
'Give me a location, like .weather London, or tell me where you live by saying .setlocation London, for example.')
|
||||||
else:
|
else:
|
||||||
location = location.strip()
|
location = location.strip()
|
||||||
|
|
|
@ -16,6 +16,9 @@ import wolframalpha
|
||||||
@example('.wa 2+2', '[W|A] 2+2 = 4')
|
@example('.wa 2+2', '[W|A] 2+2 = 4')
|
||||||
@example('.wa python language release date', '[W|A] Python | date introduced = 1991')
|
@example('.wa python language release date', '[W|A] Python | date introduced = 1991')
|
||||||
def wa_command(bot, trigger):
|
def wa_command(bot, trigger):
|
||||||
|
"""
|
||||||
|
Queries WolframAlpha.
|
||||||
|
"""
|
||||||
msg = None
|
msg = None
|
||||||
if not trigger.group(2):
|
if not trigger.group(2):
|
||||||
msg = 'You must provide a query.'
|
msg = 'You must provide a query.'
|
||||||
|
|
123
run_script.py
123
run_script.py
|
@ -1,5 +1,5 @@
|
||||||
#!/usr/bin/env python2.7
|
#! /usr/bin/env python3
|
||||||
# coding=utf-8
|
# -*- coding: utf-8 -*-
|
||||||
"""
|
"""
|
||||||
Sopel - An IRC Bot
|
Sopel - An IRC Bot
|
||||||
Copyright 2008, Sean B. Palmer, inamidst.com
|
Copyright 2008, Sean B. Palmer, inamidst.com
|
||||||
|
@ -8,18 +8,8 @@ Licensed under the Eiffel Forum License 2.
|
||||||
|
|
||||||
http://sopel.chat
|
http://sopel.chat
|
||||||
"""
|
"""
|
||||||
from __future__ import unicode_literals, absolute_import, print_function, division
|
|
||||||
|
|
||||||
import sys
|
import sys
|
||||||
from tools import stderr
|
from tools import stderr
|
||||||
|
|
||||||
if sys.version_info < (2, 7):
|
|
||||||
stderr('Error: Requires Python 2.7 or later. Try python2.7 sopel')
|
|
||||||
sys.exit(1)
|
|
||||||
if sys.version_info.major == 3 and sys.version_info.minor < 3:
|
|
||||||
stderr('Error: When running on Python 3, Python 3.3 is required.')
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
import os
|
import os
|
||||||
import argparse
|
import argparse
|
||||||
import signal
|
import signal
|
||||||
|
@ -57,36 +47,71 @@ def main(argv=None):
|
||||||
global homedir
|
global homedir
|
||||||
# Step One: Parse The Command Line
|
# Step One: Parse The Command Line
|
||||||
try:
|
try:
|
||||||
parser = argparse.ArgumentParser(description='Sopel IRC Bot',
|
parser = argparse.ArgumentParser(
|
||||||
|
description='Sopel IRC Bot',
|
||||||
usage='%(prog)s [options]')
|
usage='%(prog)s [options]')
|
||||||
parser.add_argument('-c', '--config', metavar='filename',
|
parser.add_argument(
|
||||||
|
'-c',
|
||||||
|
'--config',
|
||||||
|
metavar='filename',
|
||||||
help='use a specific configuration file')
|
help='use a specific configuration file')
|
||||||
parser.add_argument("-d", '--fork', action="store_true",
|
parser.add_argument(
|
||||||
dest="daemonize", help="Daemonize sopel")
|
"-d",
|
||||||
parser.add_argument("-q", '--quit', action="store_true", dest="quit",
|
'--fork',
|
||||||
|
action="store_true",
|
||||||
|
dest="daemonize",
|
||||||
|
help="Daemonize sopel")
|
||||||
|
parser.add_argument(
|
||||||
|
"-q",
|
||||||
|
'--quit',
|
||||||
|
action="store_true",
|
||||||
|
dest="quit",
|
||||||
help="Gracefully quit Sopel")
|
help="Gracefully quit Sopel")
|
||||||
parser.add_argument("-k", '--kill', action="store_true", dest="kill",
|
parser.add_argument(
|
||||||
|
"-k",
|
||||||
|
'--kill',
|
||||||
|
action="store_true",
|
||||||
|
dest="kill",
|
||||||
help="Kill Sopel")
|
help="Kill Sopel")
|
||||||
parser.add_argument("-l", '--list', action="store_true",
|
parser.add_argument(
|
||||||
|
"-l",
|
||||||
|
'--list',
|
||||||
|
action="store_true",
|
||||||
dest="list_configs",
|
dest="list_configs",
|
||||||
help="List all config files found")
|
help="List all config files found")
|
||||||
parser.add_argument("-m", '--migrate', action="store_true",
|
parser.add_argument(
|
||||||
|
"-m",
|
||||||
|
'--migrate',
|
||||||
|
action="store_true",
|
||||||
dest="migrate_configs",
|
dest="migrate_configs",
|
||||||
help="Migrate config files to the new format")
|
help="Migrate config files to the new format")
|
||||||
parser.add_argument('--quiet', action="store_true", dest="quiet",
|
parser.add_argument(
|
||||||
|
'--quiet',
|
||||||
|
action="store_true",
|
||||||
|
dest="quiet",
|
||||||
help="Supress all output")
|
help="Supress all output")
|
||||||
parser.add_argument('-w', '--configure-all', action='store_true',
|
parser.add_argument(
|
||||||
dest='wizard', help='Run the configuration wizard.')
|
'-w',
|
||||||
parser.add_argument('--configure-modules', action='store_true',
|
'--configure-all',
|
||||||
dest='mod_wizard', help=(
|
action='store_true',
|
||||||
'Run the configuration wizard, but only for the '
|
dest='wizard',
|
||||||
'module configuration options.'))
|
help='Run the configuration wizard.')
|
||||||
parser.add_argument('-v', '--version', action="store_true",
|
parser.add_argument(
|
||||||
dest="version", help="Show version number and exit")
|
'--configure-modules',
|
||||||
|
action='store_true',
|
||||||
|
dest='mod_wizard',
|
||||||
|
help='Run the configuration wizard, but only for the module ' + \
|
||||||
|
'configuration options.')
|
||||||
|
parser.add_argument(
|
||||||
|
'-v',
|
||||||
|
'--version',
|
||||||
|
action="store_true",
|
||||||
|
dest="version",
|
||||||
|
help="Show version number and exit")
|
||||||
if argv:
|
if argv:
|
||||||
opts = parser.parse_args(argv)
|
args = parser.parse_args(argv)
|
||||||
else:
|
else:
|
||||||
opts = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
# Step Two: "Do not run as root" checks.
|
# Step Two: "Do not run as root" checks.
|
||||||
try:
|
try:
|
||||||
|
@ -100,21 +125,21 @@ def main(argv=None):
|
||||||
stderr('Error: Do not run Sopel as Administrator.')
|
stderr('Error: Do not run Sopel as Administrator.')
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
if opts.version:
|
if args.version:
|
||||||
py_ver = '%s.%s.%s' % (sys.version_info.major,
|
py_ver = '%s.%s.%s' % (sys.version_info.major,
|
||||||
sys.version_info.minor,
|
sys.version_info.minor,
|
||||||
sys.version_info.micro)
|
sys.version_info.micro)
|
||||||
print('Sopel %s (running on python %s)' % (__version__, py_ver))
|
print('Sopel %s (running on python %s)' % (__version__, py_ver))
|
||||||
print('http://sopel.chat/')
|
print('http://sopel.chat/')
|
||||||
return
|
return
|
||||||
elif opts.wizard:
|
elif args.wizard:
|
||||||
_wizard('all', opts.config)
|
_wizard('all', args.config)
|
||||||
return
|
return
|
||||||
elif opts.mod_wizard:
|
elif args.mod_wizard:
|
||||||
_wizard('mod', opts.config)
|
_wizard('mod', args.config)
|
||||||
return
|
return
|
||||||
|
|
||||||
if opts.list_configs:
|
if args.list_configs:
|
||||||
configs = enumerate_configs()
|
configs = enumerate_configs()
|
||||||
print('Config files in ~/.sopel:')
|
print('Config files in ~/.sopel:')
|
||||||
if len(configs) is 0:
|
if len(configs) is 0:
|
||||||
|
@ -125,7 +150,7 @@ def main(argv=None):
|
||||||
print('-------------------------')
|
print('-------------------------')
|
||||||
return
|
return
|
||||||
|
|
||||||
config_name = opts.config or 'default'
|
config_name = args.config or 'default'
|
||||||
|
|
||||||
configpath = find_config(config_name)
|
configpath = find_config(config_name)
|
||||||
if not os.path.isfile(configpath):
|
if not os.path.isfile(configpath):
|
||||||
|
@ -147,17 +172,17 @@ def main(argv=None):
|
||||||
|
|
||||||
logfile = os.path.os.path.join(config_module.core.logdir, 'stdio.log')
|
logfile = os.path.os.path.join(config_module.core.logdir, 'stdio.log')
|
||||||
|
|
||||||
config_module._is_daemonized = opts.daemonize
|
config_module._is_daemonized = args.daemonize
|
||||||
|
|
||||||
sys.stderr = tools.OutputRedirect(logfile, True, opts.quiet)
|
sys.stderr = tools.OutputRedirect(logfile, True, args.quiet)
|
||||||
sys.stdout = tools.OutputRedirect(logfile, False, opts.quiet)
|
sys.stdout = tools.OutputRedirect(logfile, False, args.quiet)
|
||||||
|
|
||||||
# Handle --quit, --kill and saving the PID to file
|
# Handle --quit, --kill and saving the PID to file
|
||||||
pid_dir = config_module.core.pid_dir
|
pid_dir = config_module.core.pid_dir
|
||||||
if opts.config is None:
|
if args.config is None:
|
||||||
pid_file_path = os.path.join(pid_dir, 'sopel.pid')
|
pid_file_path = os.path.join(pid_dir, 'sopel.pid')
|
||||||
else:
|
else:
|
||||||
basename = os.path.basename(opts.config)
|
basename = os.path.basename(args.config)
|
||||||
if basename.endswith('.cfg'):
|
if basename.endswith('.cfg'):
|
||||||
basename = basename[:-4]
|
basename = basename[:-4]
|
||||||
pid_file_path = os.path.join(pid_dir, 'sopel-%s.pid' % basename)
|
pid_file_path = os.path.join(pid_dir, 'sopel-%s.pid' % basename)
|
||||||
|
@ -168,28 +193,28 @@ def main(argv=None):
|
||||||
except ValueError:
|
except ValueError:
|
||||||
old_pid = None
|
old_pid = None
|
||||||
if old_pid is not None and tools.check_pid(old_pid):
|
if old_pid is not None and tools.check_pid(old_pid):
|
||||||
if not opts.quit and not opts.kill:
|
if not args.quit and not args.kill:
|
||||||
stderr('There\'s already a Sopel instance running with this config file')
|
stderr('There\'s already a Sopel instance running with this config file')
|
||||||
stderr('Try using the --quit or the --kill options')
|
stderr('Try using the --quit or the --kill options')
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
elif opts.kill:
|
elif args.kill:
|
||||||
stderr('Killing the sopel')
|
stderr('Killing the sopel')
|
||||||
os.kill(old_pid, signal.SIGKILL)
|
os.kill(old_pid, signal.SIGKILL)
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
elif opts.quit:
|
elif args.quit:
|
||||||
stderr('Signaling Sopel to stop gracefully')
|
stderr('Signaling Sopel to stop gracefully')
|
||||||
if hasattr(signal, 'SIGUSR1'):
|
if hasattr(signal, 'SIGUSR1'):
|
||||||
os.kill(old_pid, signal.SIGUSR1)
|
os.kill(old_pid, signal.SIGUSR1)
|
||||||
else:
|
else:
|
||||||
os.kill(old_pid, signal.SIGTERM)
|
os.kill(old_pid, signal.SIGTERM)
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
elif opts.kill or opts.quit:
|
elif args.kill or args.quit:
|
||||||
stderr('Sopel is not running!')
|
stderr('Sopel is not running!')
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
elif opts.quit or opts.kill:
|
elif args.quit or args.kill:
|
||||||
stderr('Sopel is not running!')
|
stderr('Sopel is not running!')
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
if opts.daemonize:
|
if args.daemonize:
|
||||||
child_pid = os.fork()
|
child_pid = os.fork()
|
||||||
if child_pid is not 0:
|
if child_pid is not 0:
|
||||||
sys.exit()
|
sys.exit()
|
||||||
|
|
Loading…
Reference in New Issue
Block a user