2010-05-24 13:38:06 +00:00
# -*- Encoding: utf-8 -*-
2007-02-04 17:35:40 +00:00
###
# Copyright (c) 2006-2007 Dennis Kaarsemaker
2010-05-24 13:38:06 +00:00
# Copyright (c) 2008-2010 Terence Simpson
2018-03-09 18:56:04 +00:00
# Copyright (c) 2018- Krytarik Raido
2007-02-04 17:35:40 +00:00
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of version 2 of the GNU General Public License as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
###
from supybot . commands import *
import supybot . ircmsgs as ircmsgs
import supybot . callbacks as callbacks
import supybot . registry as registry
import supybot . ircdb as ircdb
import supybot . conf as conf
2008-08-07 17:17:53 +00:00
import supybot . utils as utils
2008-10-22 17:31:40 +00:00
import supybot . ircutils as ircutils
2011-08-03 21:33:46 +00:00
import supybot . world as world
2018-05-21 22:45:04 +00:00
import sys , os , re , hashlib , random
import sqlite3 , datetime , time
2008-05-10 15:13:26 +00:00
2018-05-21 22:45:04 +00:00
_stripNickChars = """ ! " #$ % & ' ()*+,./:;<=>?@~ """
def stripNick ( nick ) :
while nick and nick [ - 1 ] in _stripNickChars :
nick = nick [ : - 1 ]
return nick
2009-10-12 18:26:35 +00:00
2020-10-23 04:23:04 +00:00
def defaultIgnored ( hostmask ) :
2018-05-21 22:45:04 +00:00
if not conf . supybot . defaultIgnore ( ) :
return False
2008-10-22 17:31:40 +00:00
try :
2018-05-21 22:45:04 +00:00
user = ircdb . users . getUser ( hostmask )
2008-10-22 17:31:40 +00:00
except KeyError :
2018-05-21 22:45:04 +00:00
return True
return False
2011-05-28 06:33:21 +00:00
2020-10-23 04:23:04 +00:00
def checkIgnored ( hostmask , channel ) :
2018-05-21 22:45:04 +00:00
try :
user = ircdb . users . getUser ( hostmask )
2020-10-26 00:23:04 +00:00
try :
if user . _checkCapability ( ' trusted ' ) :
return False
except KeyError :
pass
if user . ignore :
2018-05-21 22:45:04 +00:00
return True
except KeyError :
pass
2011-05-28 06:33:21 +00:00
if ircdb . ignores . checkIgnored ( hostmask ) :
return True
2020-10-23 04:23:04 +00:00
if channel :
c = ircdb . channels . getChannel ( channel )
2018-05-21 22:45:04 +00:00
if c . checkIgnored ( hostmask ) :
return True
return False
def checkAddressed ( text , channel ) :
if channel :
if text [ 0 ] in str ( conf . supybot . reply . whenAddressedBy . chars . get ( channel ) ) :
return True
elif text [ 0 ] in conf . supybot . reply . whenAddressedBy . chars ( ) :
2008-10-22 17:31:40 +00:00
return True
2018-05-21 22:45:04 +00:00
return False
2007-02-04 17:35:40 +00:00
# Simple wrapper class for factoids
class Factoid :
2018-03-22 20:34:04 +00:00
def __init__ ( self , name , value , author = None , added = None ,
editor = None , edited = None , popularity = None ) :
2007-02-04 17:35:40 +00:00
self . name = name ; self . value = value
self . author = author ; self . added = added
2018-03-22 20:34:04 +00:00
self . editor = editor ; self . edited = edited
2007-02-04 17:35:40 +00:00
self . popularity = popularity
class FactoidSet :
def __init__ ( self ) :
self . global_primary = self . global_secondary = \
self . channel_primary = self . channel_secondary = None
# Repeat filtering message queue
msgcache = { }
2018-05-21 22:45:04 +00:00
def queue ( irc , target , msg ) :
2011-08-03 21:33:46 +00:00
if world . testing :
# don't mess up testcases
2020-10-24 03:04:04 +00:00
irc . reply ( msg , to = target , private = True )
2011-08-03 21:33:46 +00:00
return
2007-02-04 17:35:40 +00:00
now = time . time ( )
2018-02-22 10:45:04 +00:00
for m in list ( msgcache . keys ( ) ) :
2007-02-04 17:35:40 +00:00
if msgcache [ m ] < now - 30 :
msgcache . pop ( m )
for m in msgcache :
2018-05-21 22:45:04 +00:00
if m [ 0 ] == irc and m [ 1 ] == target :
2007-02-04 17:35:40 +00:00
oldmsg = m [ 2 ]
2018-05-21 22:45:04 +00:00
if oldmsg . endswith ( msg ) :
2007-02-04 17:35:40 +00:00
break
2018-05-21 22:45:04 +00:00
if msg . endswith ( oldmsg ) and msg [ : - len ( oldmsg ) ] . endswith ( ' : ' ) :
msg = msg [ : - len ( oldmsg ) ] + ' Please see above '
2007-02-04 17:35:40 +00:00
else :
2018-05-21 22:45:04 +00:00
msgcache [ ( irc , target , msg ) ] = now
2020-10-24 03:04:04 +00:00
irc . reply ( msg , to = target , private = True )
2007-02-04 17:35:40 +00:00
def capab ( prefix , capability ) :
2018-05-21 22:45:04 +00:00
# too bad people don't use supybot's own methods,
# it would save me the trouble of hacking this up
2010-05-24 13:38:06 +00:00
if world . testing :
2018-05-21 22:45:04 +00:00
# we're running a testcase, return always True
2010-05-24 13:38:06 +00:00
return True
2008-08-07 13:31:26 +00:00
# Capability hierarchy #
if capability == " editfactoids " :
2018-05-21 22:45:04 +00:00
if capab ( prefix , " addeditors " ) :
2008-08-07 13:31:26 +00:00
return True
2018-05-21 22:45:04 +00:00
elif capability == " addeditors " :
if capab ( prefix , " admin " ) :
2008-08-07 13:31:26 +00:00
return True
2018-05-21 22:45:04 +00:00
elif capability == " admin " :
if capab ( prefix , " owner " ) :
2008-08-07 13:31:26 +00:00
return True
2018-05-21 22:45:04 +00:00
# Check capability #
try :
user = ircdb . users . getUser ( prefix )
except KeyError :
2008-08-07 13:31:26 +00:00
return False
2018-05-21 22:45:04 +00:00
if capability in list ( user . capabilities ) :
return True
return False
2007-02-04 17:35:40 +00:00
2018-05-21 22:45:04 +00:00
def get_factoid_label ( n , v ) :
2021-06-13 23:56:04 +00:00
if ' <alias> ' in v :
n + = ' @ ' + v [ v . find ( ' <alias> ' ) + 7 : ] . strip ( )
if v . startswith ( ' <deleted> ' ) :
n + = ' * '
return n
def get_alias_label ( n , v ) :
if v . startswith ( ' <deleted> ' ) :
2018-05-21 22:45:04 +00:00
n + = ' * '
return n
2008-10-22 17:31:40 +00:00
2018-05-21 22:45:04 +00:00
# This regexp should match most URLs in the format protocol://(domain|ip adress)
2010-06-30 16:06:58 +00:00
# and the special case when there's no protocol but domain starts with www.
# We do this so we can filter obvious requests with spam in them
octet = r ' (?:2(?:[0-4] \ d|5[0-5])|1 \ d \ d| \ d { 1,2}) ' # 0 - 255
ip_address = r ' %s (?: \ . %s ) {3} ' % ( octet , octet ) # 0.0.0.0 - 255.255.255.255
# Base domain regex off RFC 1034 and 1738
label = r ' [0-9a-z][-0-9a-z]*[0-9a-z]? '
domain = r ' %s (?: \ . %s )* \ .[a-z][-0-9a-z]*[a-z]? ' % ( label , label ) # like www.ubuntu.com
# complete regexp
urlRe = re . compile ( r ' (?: \ w+://(?: %s | %s )|www \ . %s ) ' % ( domain , ip_address , domain ) , re . I )
def checkUrl ( s ) :
2018-05-21 22:45:04 +00:00
""" Check if string contains something like a URL """
2010-06-30 16:06:58 +00:00
return bool ( urlRe . search ( s ) )
2018-03-09 18:56:04 +00:00
class DbWrapper ( object ) :
def __init__ ( self , db , name ) :
self . db = db
self . name = name
self . time = time . time ( )
2007-02-04 17:35:40 +00:00
class Encyclopedia ( callbacks . Plugin ) :
""" !factoid: show factoid """
def __init__ ( self , irc ) :
2018-05-21 22:45:04 +00:00
self . __parent = super ( Encyclopedia , self )
self . __parent . __init__ ( irc )
2007-02-04 17:35:40 +00:00
self . databases = { }
2009-10-12 18:26:35 +00:00
self . times = { }
2007-02-04 17:35:40 +00:00
self . edits = { }
2008-01-19 17:57:32 +00:00
self . alert = False
2011-08-04 12:30:34 +00:00
self . defaultIrc = irc
2007-02-04 17:35:40 +00:00
2018-05-21 22:45:04 +00:00
class editor ( callbacks . Commands ) :
def add ( self , irc , msg , args , name ) :
""" <name>
2008-05-05 16:44:14 +00:00
2018-05-21 22:45:04 +00:00
Adds the user with the name < name > to the list of editors .
"""
if not capab ( msg . prefix , ' addeditors ' ) :
irc . errorNoCapability ( ' addeditors ' )
return
try :
user = ircdb . users . getUser ( name )
user . addCapability ( ' editfactoids ' )
irc . replySuccess ( )
except :
irc . error ( ' User %s is not registered ' % name )
2021-07-23 19:13:04 +00:00
add = wrap ( add , [ ' nick ' ] )
2007-02-04 17:35:40 +00:00
2018-05-21 22:45:04 +00:00
def remove ( self , irc , msg , args , name ) :
""" <name>
2008-05-05 16:44:14 +00:00
2018-05-21 22:45:04 +00:00
Removes the user with the name < name > from the list of editors .
"""
if not capab ( msg . prefix , ' addeditors ' ) :
irc . errorNoCapability ( ' addeditors ' )
return
try :
user = ircdb . users . getUser ( name )
user . removeCapability ( ' editfactoids ' )
irc . replySuccess ( )
except :
irc . error ( ' User %s is not registered or not an editor ' % name )
2021-07-23 19:13:04 +00:00
remove = wrap ( remove , [ ' nick ' ] )
2007-02-04 17:35:40 +00:00
def editors ( self , irc , msg , args ) :
2021-06-12 22:23:04 +00:00
""" takes no arguments
2008-05-05 16:44:14 +00:00
Lists all the users who are in the list of editors .
"""
2018-02-22 10:45:04 +00:00
irc . reply ( ' , ' . join ( [ u . name for u in list ( ircdb . users . users . values ( ) ) if capab ( u . name , ' editfactoids ' ) ] ) , private = True )
2007-02-04 17:35:40 +00:00
editors = wrap ( editors )
def moderators ( self , irc , msg , args ) :
2021-06-12 22:23:04 +00:00
""" takes no arguments
2008-05-05 16:44:14 +00:00
Lists all the users who can add users to the list of editors .
"""
2018-02-22 10:45:04 +00:00
irc . reply ( ' , ' . join ( [ u . name for u in list ( ircdb . users . users . values ( ) ) if capab ( u . name , ' addeditors ' ) ] ) , private = True )
2007-02-04 17:35:40 +00:00
moderators = wrap ( moderators )
2020-10-23 04:23:04 +00:00
def get_target ( self , irc , nick , text , target ) :
2018-05-21 22:45:04 +00:00
ret , retmsg = text , ' '
orig_target = target
# Test for factoid creation/edit before continuing
if re . match ( r ' \ S+ \ s+is( \ s+| \ s*<(reply|alias|sed)> \ s*) \ S+ ' , text , re . I ) :
return ret , target , retmsg
if ' | ' in text :
retmsg = text [ text . find ( ' | ' ) + 1 : ] . lstrip ( ' | ' ) . strip ( )
ret = text [ : text . find ( ' | ' ) ] . strip ( )
if retmsg :
if retmsg . lower ( ) == " me " :
retmsg = nick
retmsg = " %s : " % retmsg
elif ' > ' in text :
target = text [ text . find ( ' > ' ) + 1 : ] . lstrip ( ' > ' ) . strip ( )
ret = text [ : text . find ( ' > ' ) ] . strip ( )
if target :
# Take the first "nick" and strip off bad chars
target = stripNick ( target . split ( ) [ 0 ] )
if target . lower ( ) == " me " :
target = nick
2009-10-12 18:26:35 +00:00
retmsg = " < %s > wants you to know: " % nick
2018-05-21 22:45:04 +00:00
else :
match = re . match ( r ' ^tell \ s+(?P<target> \ S+) \ s+about \ s+(?P<ret>.+)$ ' , text , re . I )
if match :
target = match . group ( ' target ' )
ret = match . group ( ' ret ' )
if target . lower ( ) == " me " :
target = nick
retmsg = " < %s > wants you to know: " % nick
2009-10-12 18:26:35 +00:00
2020-10-23 04:23:04 +00:00
if target . lower ( ) != orig_target . lower ( ) and irc . isChannel ( target ) :
2007-02-04 17:35:40 +00:00
target = orig_target
2018-05-21 22:45:04 +00:00
retmsg = " (Forwarding to channels is not permitted) "
elif nick . lower ( ) in ( target . lower ( ) , retmsg [ : - 2 ] . lower ( ) ) \
and nick . lower ( ) != orig_target . lower ( ) :
2007-02-04 17:35:40 +00:00
target = nick
2018-05-21 22:45:04 +00:00
retmsg = " (In the future, please use a private message to investigate) "
2007-02-04 17:35:40 +00:00
2018-05-21 22:45:04 +00:00
return ret , target , retmsg
2007-02-04 17:35:40 +00:00
def get_db ( self , channel ) :
2018-05-21 22:45:04 +00:00
db = self . registryValue ( ' database ' , channel )
if channel in self . databases \
and ( self . databases [ channel ] . time < time . time ( ) - 3600 \
or self . databases [ channel ] . name != db ) :
self . databases [ channel ] . db . close ( )
self . databases . pop ( channel )
2007-02-04 17:35:40 +00:00
if channel not in self . databases :
2018-03-09 18:56:04 +00:00
self . databases [ channel ] = DbWrapper (
sqlite3 . connect ( os . path . join ( self . registryValue ( ' datadir ' ) , ' %s .db ' % db ) ) , db )
return self . databases [ channel ] . db
2009-10-12 18:26:35 +00:00
2018-05-21 22:45:04 +00:00
def get_factoids ( self , name , channel , display = None ) :
2007-02-04 17:35:40 +00:00
factoids = FactoidSet ( )
2021-04-22 19:13:04 +00:00
if not display or display == ' alias ' :
deleted = False
else :
deleted = True
factoids . global_primary = self . get_single_factoid ( channel , name , deleted )
factoids . global_secondary = self . get_single_factoid ( channel , name + ' -also ' , deleted )
if channel and not display :
factoids . channel_primary = self . get_single_factoid ( channel , name + ' - ' + channel . lower ( ) , deleted )
factoids . channel_secondary = self . get_single_factoid ( channel , name + ' - ' + channel . lower ( ) + ' -also ' , deleted )
2018-05-21 22:45:04 +00:00
if not display :
self . increment_factoid_popularity ( factoids , channel )
2021-04-22 19:13:04 +00:00
factoids . global_primary = self . resolve_alias ( channel , factoids . global_primary )
factoids . global_secondary = self . resolve_alias ( channel , factoids . global_secondary )
2018-05-21 22:45:04 +00:00
if channel :
factoids . channel_primary = self . resolve_alias ( channel , factoids . channel_primary )
factoids . channel_secondary = self . resolve_alias ( channel , factoids . channel_secondary )
elif display == ' info ' :
2021-04-22 19:13:04 +00:00
factoids . global_primary = self . factoid_info ( channel , factoids . global_primary )
factoids . global_secondary = self . factoid_info ( channel , factoids . global_secondary )
2018-05-21 22:45:04 +00:00
if channel :
factoids . channel_primary = self . factoid_info ( channel , factoids . channel_primary )
factoids . channel_secondary = self . factoid_info ( channel , factoids . channel_secondary )
2007-02-04 17:35:40 +00:00
return factoids
2018-05-21 22:45:04 +00:00
def increment_factoid_popularity ( self , factoids , channel ) :
for order in ( ' primary ' , ' secondary ' ) :
for loc in ( ' channel ' , ' global ' ) :
key = ' %s _ %s ' % ( loc , order )
if getattr ( factoids , key ) :
factoid = getattr ( factoids , key )
if isinstance ( factoid , Factoid ) :
db = self . get_db ( channel )
cur = db . cursor ( )
cur . execute ( " UPDATE facts SET popularity = ? WHERE name = ? " , ( factoid . popularity + 1 , factoid . name ) )
db . commit ( )
break
2007-02-04 17:35:40 +00:00
def get_single_factoid ( self , channel , name , deleted = False ) :
db = self . get_db ( channel )
cur = db . cursor ( )
if deleted :
2018-03-22 20:34:04 +00:00
cur . execute ( " SELECT name, value, author, added, editor, edited, popularity FROM facts WHERE name = ? " , ( name . lower ( ) , ) )
2007-02-04 17:35:40 +00:00
else :
2018-03-22 20:34:04 +00:00
cur . execute ( " SELECT name, value, author, added, editor, edited, popularity FROM facts WHERE name = ? AND value NOT LIKE ' <deleted> %% ' " , ( name . lower ( ) , ) )
2007-02-04 17:35:40 +00:00
factoids = cur . fetchall ( )
2018-05-21 22:45:04 +00:00
if factoids :
return Factoid ( * factoids [ 0 ] )
2007-02-04 17:35:40 +00:00
def resolve_alias ( self , channel , factoid , loop = 0 ) :
2018-05-21 22:45:04 +00:00
if not factoid :
return
2007-02-04 17:35:40 +00:00
if loop > = 10 :
2018-05-21 22:45:04 +00:00
return " Error: Infinite <alias> loop detected "
if factoid . name in self . registryValue ( ' alert ' , channel ) :
self . alert = True
if factoid . value . startswith ( ' <alias> ' ) :
alias_name = factoid . value [ 7 : ] . strip ( )
factoids = self . get_factoids ( alias_name , channel , display = ' alias ' )
for x in ( ' channel_primary ' , ' global_primary ' ) :
if getattr ( factoids , x ) :
return self . resolve_alias ( channel , getattr ( factoids , x ) , loop + 1 )
return " Error: Unresolvable <alias> to ' %s ' " % alias_name
2007-02-04 17:35:40 +00:00
else :
return factoid
def factoid_info ( self , channel , factoid ) :
if not factoid :
return
2018-05-21 22:45:04 +00:00
ret = [ get_factoid_label ( factoid . name , factoid . value ) ]
2021-06-13 23:56:04 +00:00
aliases = self . get_aliases ( channel , factoid )
if aliases :
ret . append ( " aliases: %s " % ' , ' . join ( [ get_alias_label ( x [ 0 ] , x [ 1 ] ) for x in aliases ] ) )
2018-05-21 22:45:04 +00:00
else :
ret . append ( " no aliases " )
2007-02-04 17:35:40 +00:00
# Author info
2018-05-21 22:45:04 +00:00
ret . append ( " added by %s on %s " % ( factoid . author [ : factoid . author . find ( ' ! ' ) ] , factoid . added [ : factoid . added . rfind ( ' . ' ) ] ) )
2018-03-22 20:34:04 +00:00
if factoid . editor :
ret . append ( " last edited by %s on %s " % ( factoid . editor [ : factoid . editor . find ( ' ! ' ) ] , factoid . edited [ : factoid . edited . rfind ( ' . ' ) ] ) )
2018-05-21 22:45:04 +00:00
# Popularity info
ret . append ( " requested %d times " % factoid . popularity )
return ' - ' . join ( ret )
2007-02-04 17:35:40 +00:00
2021-06-13 23:56:04 +00:00
def get_aliases ( self , channel , factoid ) :
db = self . get_db ( channel )
cur = db . cursor ( )
cur . execute ( " SELECT name, value FROM facts WHERE value LIKE ? " , ( ' %% <alias>_ %s ' % factoid . name , ) )
data = cur . fetchall ( )
return data
def check_aliases ( self , channel , factoid , oldvalue , etype ) :
2007-02-04 17:35:40 +00:00
now = time . time ( )
2018-02-22 10:45:04 +00:00
for e in list ( self . edits . keys ( ) ) :
2018-05-21 22:45:04 +00:00
if self . edits [ e ] < now - 10 :
2007-02-04 17:35:40 +00:00
self . edits . pop ( e )
# Was the old value an alias?
2021-06-13 23:56:04 +00:00
if etype == ' edit ' and oldvalue . startswith ( ' <alias> ' ) \
and not factoid . value . startswith ( ' <alias> ' ) :
2007-02-04 17:35:40 +00:00
if factoid . name not in self . edits :
self . edits [ factoid . name ] = now
2018-05-21 22:45:04 +00:00
return " You are editing an alias. Please repeat the edit command within the next 10 seconds to confirm "
2007-02-04 17:35:40 +00:00
# Do some alias resolving
2021-06-13 23:56:04 +00:00
if factoid . value . startswith ( ' <alias> ' ) and etype != ' delete ' :
2018-05-21 22:45:04 +00:00
alias_name = factoid . value [ 7 : ] . strip ( )
alias = self . get_single_factoid ( channel , alias_name )
2007-02-04 17:35:40 +00:00
if not alias :
2018-05-21 22:45:04 +00:00
return " Factoid ' %s ' does not exist " % alias_name
2007-02-04 17:35:40 +00:00
alias = self . resolve_alias ( channel , factoid )
2018-05-21 22:45:04 +00:00
if not isinstance ( alias , Factoid ) :
return alias
2008-10-22 17:31:40 +00:00
2010-06-09 14:07:52 +00:00
def inFilter ( self , irc , msg ) :
2019-05-31 04:50:04 +00:00
if not ( msg . prefix and msg . args ) :
2018-03-27 21:56:04 +00:00
return msg
2020-10-19 18:56:04 +00:00
if not ircutils . isUserHostmask ( msg . prefix ) :
return msg
2020-10-23 04:23:04 +00:00
if not defaultIgnored ( msg . prefix ) :
2018-05-21 22:45:04 +00:00
return msg
2020-10-23 04:23:04 +00:00
if checkIgnored ( msg . prefix , msg . channel ) :
2018-05-21 22:45:04 +00:00
return msg
if msg . command == " PRIVMSG " :
self . doPrivmsg ( irc , msg )
return msg
2010-06-09 14:07:52 +00:00
2007-02-04 17:35:40 +00:00
def doPrivmsg ( self , irc , msg ) :
2013-08-09 11:52:22 +00:00
queue_msg = True # Queue message or send directly
2018-05-21 22:45:04 +00:00
def noqueue ( irc , target , msg ) :
2020-10-24 03:04:04 +00:00
irc . reply ( msg , to = target , private = True )
2018-05-21 22:45:04 +00:00
def myqueue ( irc , target , msg ) :
( queue if queue_msg else noqueue ) ( irc , target , msg )
2013-08-09 11:52:22 +00:00
2007-02-04 17:35:40 +00:00
# Filter CTCP
if chr ( 1 ) in msg . args [ 1 ] :
return
2018-02-22 10:45:04 +00:00
if sys . version_info < ( 3 , 0 ) :
2018-05-21 22:45:04 +00:00
text = msg . args [ 1 ] . decode ( ' utf-8 ' ) . strip ( )
2018-02-22 10:45:04 +00:00
else :
2018-05-21 22:45:04 +00:00
text = msg . args [ 1 ] . strip ( )
if not text :
return
target = msg . args [ 0 ]
2020-10-23 04:23:04 +00:00
channel = msg . channel
2018-05-21 22:45:04 +00:00
if checkAddressed ( text , channel ) :
return
if not self . registryValue ( ' enabled ' , channel ) :
return
if not channel :
2021-09-21 14:23:04 +00:00
args = text . lower ( ) . split ( None , 2 )
2018-05-21 22:45:04 +00:00
for c in irc . callbacks :
2021-09-21 14:23:04 +00:00
if ( args [ 0 ] == c . name ( ) . lower ( ) and len ( args ) > 1
and c . isCommandMethod ( args [ 1 ] ) ) \
or c . isCommandMethod ( args [ 0 ] ) :
2018-05-21 22:45:04 +00:00
return
2020-11-06 04:34:04 +00:00
prefixchar = self . registryValue ( ' prefixchar ' , channel )
if text [ 0 ] == prefixchar :
text = text [ 1 : ] . strip ( )
2020-12-10 18:23:04 +00:00
elif re . match ( r ' ^ %s [ \ W \ s] \ s* %s ' % ( re . escape ( irc . nick ) , re . escape ( prefixchar ) ) , text , re . I ) :
text = re . sub ( r ' ^ %s [ \ W \ s] \ s* %s \ s* ' % ( re . escape ( irc . nick ) , re . escape ( prefixchar ) ) , ' ' , text , flags = re . I )
2018-05-21 22:45:04 +00:00
elif channel :
2014-06-02 01:24:20 +00:00
return
2007-02-04 17:35:40 +00:00
if not text :
return
2019-07-08 00:45:04 +00:00
if text . split ( ) [ 0 ] . lower ( ) in self . registryValue ( ' ignores ' , channel ) :
2018-05-21 22:45:04 +00:00
return
display = None # Set display type
edit = None
if not channel :
2007-02-04 17:35:40 +00:00
target = msg . nick
2018-05-21 22:45:04 +00:00
# Get display type
while text and text [ 0 ] in ' -+ ' :
2007-02-04 17:35:40 +00:00
if text [ 0 ] == ' - ' :
2018-05-21 22:45:04 +00:00
display = ' info '
elif text [ 0 ] == ' + ' :
display = ' raw '
text = text [ 1 : ] . strip ( )
2007-02-04 17:35:40 +00:00
if not text :
return
2018-05-21 22:45:04 +00:00
2021-04-22 19:13:04 +00:00
irc = callbacks . ReplyIrcProxy ( irc , msg )
2021-06-13 21:34:04 +00:00
ret , retmsg = ' ' , ' '
2018-05-21 22:45:04 +00:00
2007-02-04 17:35:40 +00:00
# Now switch between actions
2021-06-13 21:34:04 +00:00
if display :
ret = self . factoid_lookup ( text , channel , msg . nick , display )
2008-10-22 17:31:40 +00:00
else :
2021-06-13 21:34:04 +00:00
term = self . get_target ( irc , msg . nick , text , target )
lower_term = term [ 0 ] . lower ( )
if lower_term == " search " : # Usage info for the !search command
ret = " Search factoids for term: !search <term> "
target = term [ 1 ]
retmsg = term [ 2 ]
elif re . match ( r " ^seen \ b " , lower_term ) : # Some people expect a '!seen <nick>' command
ret = " I have no seen command "
retmsg = " %s : " % msg . nick if term [ 2 ] else ' ' # Redirect back at the caller, rather than the target
elif lower_term . startswith ( " google " ) : # Some people expect a '!google <term...>' command
ret = " I have no google command, use https://www.google.com/ "
retmsg = " %s : " % msg . nick if term [ 2 ] else ' ' # Redirect back at the caller, rather than the target
elif re . match ( r " ^what ' ?s? \ b " , lower_term ) : # Try and catch people saying "ubottu: what is ...?"
ret = " I am only a bot, please don ' t think I ' m intelligent :) "
retmsg = " %s : " % msg . nick if channel else ' '
# Lookup, search or edit?
elif lower_term . startswith ( ' search ' ) :
ret = self . search_factoid ( text [ 7 : ] . strip ( ) , channel )
elif lower_term . startswith ( ' delete ' ) :
edit = ' delete '
elif re . match ( r ' ^no[ \ s,]+[^| \ s]+[^|]*? \ s+is( \ s+| \ b) \ S+ ' , text , re . I ) \
or re . match ( r ' ^[^| \ s]+[^|]*? \ s*( \ s+is \ s*<sed>|=~|~=) \ s* \ S+ ' , text , re . I ) \
or lower_term . startswith ( ( ' forget ' , ' unforget ' ) ) :
edit = ' edit '
elif re . match ( r ' ^[^| \ s]+[^|]*? \ s+is( \ s+| \ b) \ S+ ' , text , re . I ) :
edit = ' add '
else :
2021-05-26 01:45:04 +00:00
text , target , retmsg = term
2021-06-13 21:34:04 +00:00
ret = self . factoid_lookup ( text , channel , msg . nick , display )
2018-05-21 22:45:04 +00:00
if edit :
check = getattr ( self , ' factoid_ %s _check ' % edit ) ( text , channel , msg . nick )
if not isinstance ( check , tuple ) :
ret = check
elif not ( capab ( msg . prefix , ' editfactoids ' ) \
or ( channel in self . registryValue ( ' editchannel ' ) \
and capab ( msg . prefix , ' restricted-editor ' ) ) ) :
if len ( check [ 1 ] . name . split ( ) ) > 4 :
ret = " I am only a bot, please don ' t think I ' m intelligent :) "
retmsg = " %s : " % msg . nick if channel else ' '
else :
relaychan = self . registryValue ( ' relaychannel ' , channel )
if channel and channel == relaychan :
irc . reply ( " %s : Your edit request will be attended to as soon as possible. " % msg . nick
+ " Thank you for your attention to detail " , to = channel )
2007-02-04 17:35:40 +00:00
else :
2018-05-21 22:45:04 +00:00
irc . reply ( " Your edit request has been forwarded to %s . " % relaychan
+ " Thank you for your attention to detail " , private = True )
irc . reply ( " In %s , %s said: %s " % ( msg . args [ 0 ] , msg . nick , msg . args [ 1 ] ) , to = relaychan )
2018-03-22 20:34:04 +00:00
self . log_request ( check [ 0 ] , check [ 1 ] , channel , msg . prefix )
2007-02-04 17:35:40 +00:00
return
else :
2018-05-21 22:45:04 +00:00
queue_msg = False
2018-03-22 20:34:04 +00:00
getattr ( self , ' factoid_ %s ' % edit ) ( check [ 1 ] , channel , msg . prefix )
ret = check [ 2 ]
2007-02-04 17:35:40 +00:00
if not ret :
2018-05-21 22:45:04 +00:00
if len ( text . split ( ) ) > 4 :
ret = " I am only a bot, please don ' t think I ' m intelligent :) "
retmsg = " %s : " % msg . nick if channel else ' '
else :
ret = self . registryValue ( ' notfoundmsg ' )
if ret . count ( ' % ' ) == ret . count ( ' %s ' ) == 1 :
if sys . version_info < ( 3 , 0 ) :
ret = ret % repr ( text ) . lstrip ( ' u ' )
else :
ret = ret % repr ( text )
2020-10-19 20:04:04 +00:00
if not channel or self . registryValue ( ' privateNotFound ' , channel ) :
myqueue ( irc , msg . nick , ret )
else :
myqueue ( irc , channel , ret )
return
2018-05-21 22:45:04 +00:00
2018-02-22 10:45:04 +00:00
if not isinstance ( ret , list ) :
2013-08-09 11:52:22 +00:00
myqueue ( irc , target , retmsg + ret )
2007-02-04 17:35:40 +00:00
else :
2008-01-19 17:57:32 +00:00
if self . alert :
2018-05-21 22:45:04 +00:00
if channel and not channel . endswith ( ' bots ' ) :
irc . reply ( ' %s called the ops in %s ( %s ) ' % ( msg . nick , msg . args [ 0 ] , retmsg ) ,
to = self . registryValue ( ' relayChannel ' , channel ) )
2008-01-19 17:57:32 +00:00
self . alert = False
2018-05-21 22:45:04 +00:00
if retmsg and checkUrl ( retmsg ) :
# !ops factoid called with a URL, most likely spam
return
2021-06-13 21:34:04 +00:00
if channel and not irc . isChannel ( target ) \
2020-10-19 20:04:04 +00:00
and target in irc . state . channels [ channel ] . users :
myqueue ( irc , channel , " %s : Please see my private message " % target )
2018-05-21 22:45:04 +00:00
myqueue ( irc , target , retmsg + ret [ 0 ] )
2007-02-04 17:35:40 +00:00
for r in ret [ 1 : ] :
2013-08-09 11:52:22 +00:00
myqueue ( irc , target , r )
2007-02-04 17:35:40 +00:00
2010-10-16 04:20:53 +00:00
def doPart ( self , irc , msg ) :
2010-10-16 15:15:56 +00:00
if len ( msg . args ) < 2 or not msg . args [ 1 ] . startswith ( ' requested by ' ) :
2010-10-16 04:20:53 +00:00
return
channel , reason = msg . args
reason = reason [ reason . find ( ' ( ' ) + 1 : - 1 ] # get the text between ()
self . _forcedFactoid ( irc , channel , msg . nick , reason )
def doKick ( self , irc , msg ) :
channel , nick , reason = msg . args
self . _forcedFactoid ( irc , channel , nick , reason )
def _forcedFactoid ( self , irc , channel , nick , reason ) :
if not self . registryValue ( ' forcedFactoid ' , channel ) :
return
2018-05-21 22:45:04 +00:00
factoidRe = re . compile ( r ' %s \ w+ \ b ' % re . escape ( self . registryValue ( ' prefixchar ' , channel ) ) )
2010-10-16 14:55:02 +00:00
factoids = factoidRe . findall ( reason )
if not factoids :
2010-10-16 04:20:53 +00:00
return
2010-10-16 14:55:02 +00:00
L = [ ]
2018-05-21 22:45:04 +00:00
for fact in factoids :
L . extend ( self . factoid_lookup ( fact [ 1 : ] , channel , nick ) )
2010-10-16 14:55:02 +00:00
for s in L :
2020-10-24 03:04:04 +00:00
irc . reply ( s , to = nick , private = True )
2010-10-16 04:20:53 +00:00
2018-03-22 20:34:04 +00:00
def factoid_delete ( self , factoid , channel , editor ) :
edited = str ( datetime . datetime . utcnow ( ) )
self . log_edit ( ' delete ' , factoid , channel , editor , edited )
2007-02-04 17:35:40 +00:00
db = self . get_db ( channel )
cs = db . cursor ( )
2018-05-21 22:45:04 +00:00
cs . execute ( " DELETE FROM facts WHERE name = ? " , ( factoid . name , ) )
db . commit ( )
2008-07-20 15:59:27 +00:00
2018-05-21 22:45:04 +00:00
def factoid_delete_check ( self , text , channel , editor ) :
if not text . lower ( ) . startswith ( ' delete ' ) :
return
name = text [ 7 : ] . strip ( )
2021-04-22 19:13:04 +00:00
factoid = self . get_single_factoid ( channel , name , deleted = True )
2018-05-21 22:45:04 +00:00
if not factoid :
return " I know nothing about ' %s ' yet, %s " % ( name , editor )
2021-06-13 23:56:04 +00:00
aliases = self . get_aliases ( channel , factoid )
if aliases :
return " Factoid ' %s ' has aliases still, %s " % ( name , editor )
2018-05-21 22:45:04 +00:00
retmsg = " I ' ll delete that, %s " % editor
2021-06-13 23:56:04 +00:00
ret = self . check_aliases ( channel , factoid , None , ' delete ' )
2018-05-21 22:45:04 +00:00
if ret :
return ret
2018-03-22 20:34:04 +00:00
return ' delete ' , factoid , retmsg
2008-05-10 15:13:26 +00:00
2018-03-22 20:34:04 +00:00
def factoid_edit ( self , factoid , channel , editor ) :
edited = str ( datetime . datetime . utcnow ( ) )
self . log_edit ( ' edit ' , factoid , channel , editor , edited )
2018-05-21 22:45:04 +00:00
db = self . get_db ( channel )
cs = db . cursor ( )
2018-03-22 20:34:04 +00:00
cs . execute ( " UPDATE facts SET value = ?, editor = ?, edited = ? WHERE name = ? " ,
( factoid . value , editor , edited , factoid . name ) )
2018-05-21 22:45:04 +00:00
db . commit ( )
2007-02-04 17:35:40 +00:00
2018-05-21 22:45:04 +00:00
def factoid_edit_check ( self , text , channel , editor ) :
2021-06-13 23:56:04 +00:00
factoid = value = oldvalue = retmsg = None
etype = ' edit '
2007-02-04 17:35:40 +00:00
if text . lower ( ) [ : 3 ] in ( ' no ' , ' no, ' ) :
2018-05-21 22:45:04 +00:00
match = re . match ( r ' ^no[ \ s,]+(?P<name> \ S+.*?) \ s+is(?: \ s+| \ b)(?P<value>.*? \ S+)$ ' , text , re . I )
if not match :
2007-02-04 17:35:40 +00:00
return
2018-05-21 22:45:04 +00:00
name = match . group ( ' name ' )
value = match . group ( ' value ' )
factoid = self . get_single_factoid ( channel , name )
if not factoid :
return " I know nothing about ' %s ' yet, %s " % ( name , editor )
if value == factoid . value :
return " Nothing changed there "
2021-06-13 23:56:04 +00:00
oldvalue = factoid . value
2018-03-22 20:34:04 +00:00
factoid . value = value
2018-05-21 22:45:04 +00:00
retmsg = " I ' ll remember that, %s " % editor
elif text . lower ( ) . startswith ( ' forget ' ) :
2021-06-13 23:56:04 +00:00
etype = ' forget '
2018-05-21 22:45:04 +00:00
name = text [ 7 : ] . strip ( )
2007-02-04 17:35:40 +00:00
factoid = self . get_single_factoid ( channel , name )
if not factoid :
2018-05-21 22:45:04 +00:00
return " I know nothing about ' %s ' yet, %s " % ( name , editor )
2021-06-13 23:56:04 +00:00
aliases = self . get_aliases ( channel , factoid )
if aliases :
return " Factoid ' %s ' has aliases still, %s " % ( name , editor )
oldvalue = factoid . value
2018-03-22 20:34:04 +00:00
factoid . value = ' <deleted> %s ' % factoid . value
2018-05-21 22:45:04 +00:00
retmsg = " I ' ll forget that, %s " % editor
elif text . lower ( ) . startswith ( ' unforget ' ) :
2021-06-13 23:56:04 +00:00
etype = ' unforget '
2018-05-21 22:45:04 +00:00
name = text [ 9 : ] . strip ( )
factoid = self . get_single_factoid ( channel , name , deleted = True )
if not factoid :
return " I know nothing about ' %s ' at all, %s " % ( name , editor )
if not factoid . value . startswith ( ' <deleted> ' ) :
return " Factoid ' %s ' is not deleted yet, %s " % ( factoid . name , editor )
2021-06-13 23:56:04 +00:00
oldvalue = factoid . value
2018-03-22 20:34:04 +00:00
factoid . value = factoid . value [ 9 : ]
2018-05-21 22:45:04 +00:00
retmsg = " I suddenly remember ' %s ' again, %s " % ( factoid . name , editor )
else :
match = re . match ( r ' ^(?P<name> \ S+.*?) \ s*(?: \ s+is \ s*<sed>|=~|~=) \ s*(?P<regex>.*? \ S+)$ ' , text , re . I )
if not match :
return
2007-02-04 17:35:40 +00:00
# Split into name and regex
2018-05-21 22:45:04 +00:00
name = match . group ( ' name ' )
regex = match . group ( ' regex ' )
2007-02-04 17:35:40 +00:00
# Edit factoid
factoid = self . get_single_factoid ( channel , name )
if not factoid :
2018-05-21 22:45:04 +00:00
return " I know nothing about ' %s ' yet, %s " % ( name , editor )
2007-02-04 17:35:40 +00:00
# Grab the regex
if regex . startswith ( ' s ' ) :
regex = regex [ 1 : ]
if regex [ - 1 ] != regex [ 0 ] :
return " Missing end delimiter "
2018-05-21 22:45:04 +00:00
if regex . count ( regex [ 0 ] ) > 3 :
return " Too many delimiters "
if regex . count ( regex [ 0 ] ) < 3 :
return " Not enough delimiters "
2007-02-04 17:35:40 +00:00
regex , replace = regex [ 1 : - 1 ] . split ( regex [ 0 ] )
try :
regex = re . compile ( regex )
except :
return " Malformed regex "
2018-05-21 22:45:04 +00:00
value = regex . sub ( replace , factoid . value , 1 )
if value == factoid . value :
2007-02-04 17:35:40 +00:00
return " Nothing changed there "
2021-06-13 23:56:04 +00:00
oldvalue = factoid . value
2018-03-22 20:34:04 +00:00
factoid . value = value
2018-05-21 22:45:04 +00:00
retmsg = " I ' ll remember that, %s " % editor
2007-02-04 17:35:40 +00:00
2021-06-13 23:56:04 +00:00
ret = self . check_aliases ( channel , factoid , oldvalue , etype )
2007-02-04 17:35:40 +00:00
if ret :
return ret
2018-03-22 20:34:04 +00:00
return ' edit ' , factoid , retmsg
2007-02-04 17:35:40 +00:00
2018-03-22 20:34:04 +00:00
def factoid_add ( self , factoid , channel , editor ) :
edited = str ( datetime . datetime . utcnow ( ) )
self . log_edit ( ' add ' , factoid , channel , editor , edited )
2007-02-04 17:35:40 +00:00
db = self . get_db ( channel )
cs = db . cursor ( )
2018-05-21 22:45:04 +00:00
cs . execute ( " INSERT INTO facts (name, value, author, added) VALUES (?, ?, ?, ?) " ,
2018-03-22 20:34:04 +00:00
( factoid . name , factoid . value , editor , edited ) )
2018-05-21 22:45:04 +00:00
db . commit ( )
2007-02-04 17:35:40 +00:00
2018-05-21 22:45:04 +00:00
def factoid_add_check ( self , text , channel , editor ) :
match = re . match ( r ' ^(?P<name> \ S+.*?) \ s+is(?: \ s+| \ b)(?P<value>.*? \ S+)$ ' , text , re . I )
if not match :
2007-02-04 17:35:40 +00:00
return
2018-05-21 22:45:04 +00:00
name = match . group ( ' name ' ) . lower ( )
2021-05-26 01:45:04 +00:00
if re . match ( r ' .*<(reply|alias|sed)>.* ' , name ) :
return " You likely don ' t want this, %s " % editor
2018-05-21 22:45:04 +00:00
value = match . group ( ' value ' )
if value . startswith ( ( ' also ' , ' also: ' ) ) :
2007-02-04 17:35:40 +00:00
name + = ' -also '
value = value [ 5 : ] . strip ( )
if not value :
return
if self . get_single_factoid ( channel , name , deleted = True ) :
2018-05-21 22:45:04 +00:00
return " But ' %s ' already means something else! " % name
factoid = Factoid ( name , value )
retmsg = " I ' ll remember that, %s " % editor
2021-06-13 23:56:04 +00:00
ret = self . check_aliases ( channel , factoid , None , ' add ' )
2007-02-04 17:35:40 +00:00
if ret :
return ret
2018-03-22 20:34:04 +00:00
return ' add ' , factoid , retmsg
2018-05-21 22:45:04 +00:00
def factoid_lookup ( self , text , channel , nick , display = None ) :
def expand ( value ) :
for k in sorted ( list ( expandos . keys ( ) ) , reverse = True ) :
value = value . replace ( k , expandos [ k ] )
return value
expandos = {
' $who ' : nick , ' $nick ' : self . defaultIrc . nick , ' $chan ' : channel or self . defaultIrc . nick ,
' $curStable ' : self . registryValue ( ' curStable ' ) , ' $curStableLower ' : self . registryValue ( ' curStable ' ) . lower ( ) ,
' $curStableLong ' : self . registryValue ( ' curStableLong ' ) , ' $curStableNum ' : self . registryValue ( ' curStableNum ' ) ,
' $curLTS ' : self . registryValue ( ' curLTS ' ) , ' $curLTSLower ' : self . registryValue ( ' curLTS ' ) . lower ( ) ,
' $curLTSLong ' : self . registryValue ( ' curLTSLong ' ) , ' $curLTSNum ' : self . registryValue ( ' curLTSNum ' ) ,
' $curDevel ' : self . registryValue ( ' curDevel ' ) , ' $curDevelLower ' : self . registryValue ( ' curDevel ' ) . lower ( ) ,
' $curDevelLong ' : self . registryValue ( ' curDevelLong ' ) , ' $curDevelNum ' : self . registryValue ( ' curDevelNum ' )
}
factoids = self . get_factoids ( text , channel , display = display )
2007-02-04 17:35:40 +00:00
ret = [ ]
for order in ( ' primary ' , ' secondary ' ) :
for loc in ( ' channel ' , ' global ' ) :
key = ' %s _ %s ' % ( loc , order )
if getattr ( factoids , key ) :
2018-05-21 22:45:04 +00:00
factoid = getattr ( factoids , key )
if not isinstance ( factoid , Factoid ) :
ret . append ( factoid )
elif display == ' raw ' :
2011-01-26 18:44:31 +00:00
ret . append ( factoid . value )
elif factoid . value . startswith ( ' <reply> ' ) :
2018-05-21 22:45:04 +00:00
ret . append ( expand ( factoid . value [ 7 : ] . strip ( ) ) )
2007-02-04 17:35:40 +00:00
elif order == ' secondary ' :
2018-05-21 22:45:04 +00:00
ret . append ( expand ( factoid . value ) )
2007-02-04 17:35:40 +00:00
else :
2018-05-21 22:45:04 +00:00
name = factoid . name
if ' -# ' in name :
name = name [ : name . rfind ( ' -# ' ) ]
ret . append ( ' %s is %s ' % ( name , expand ( factoid . value ) ) )
if not display == ' info ' :
2007-02-10 21:47:18 +00:00
break
2007-02-04 17:35:40 +00:00
return ret
2018-03-22 20:34:04 +00:00
def log_edit ( self , etype , factoid , channel , editor , edited ) :
2018-05-21 22:45:04 +00:00
db = self . get_db ( channel )
cs = db . cursor ( )
2018-03-22 20:34:04 +00:00
cs . execute ( " INSERT INTO log (type, name, value, author, added) VALUES (?, ?, ?, ?, ?) " ,
( etype , factoid . name , factoid . value , editor , edited ) )
2018-05-21 22:45:04 +00:00
db . commit ( )
2009-10-12 18:26:35 +00:00
2018-03-22 20:34:04 +00:00
def log_request ( self , rtype , factoid , channel , requester ) :
db = self . get_db ( channel )
2009-10-12 18:26:35 +00:00
cur = db . cursor ( )
2018-05-21 22:45:04 +00:00
cur . execute ( " SELECT type, value FROM requests WHERE name = ? " , ( factoid . name , ) )
2009-10-12 18:26:35 +00:00
items = cur . fetchall ( )
2018-05-21 22:45:04 +00:00
for i in items :
if i [ 0 ] == rtype and i [ 1 ] == factoid . value :
return
2018-03-22 20:34:04 +00:00
cur . execute ( " INSERT INTO requests (type, name, value, requester, requested) VALUES (?, ?, ?, ?, ?) " ,
( rtype , factoid . name , factoid . value , requester , str ( datetime . datetime . utcnow ( ) ) ) )
2009-10-12 18:26:35 +00:00
db . commit ( )
2018-05-21 22:45:04 +00:00
def search_factoid ( self , text , channel ) :
keys = text . split ( ) [ : 5 ]
2007-02-04 17:35:40 +00:00
db = self . get_db ( channel )
cur = db . cursor ( )
2018-05-21 22:45:04 +00:00
qterms , values , ret = ' ' , [ ] , [ ]
2007-02-04 17:35:40 +00:00
for k in keys :
2018-05-21 22:45:04 +00:00
if qterms :
qterms + = ' AND '
qterms + = ' (name LIKE ? OR value LIKE ? OR value LIKE ?) '
values . extend ( [ ' %% %s %% ' % k . lower ( ) , ' %% %s %% ' % k , ' %% %s %% ' % k . lower ( ) ] )
cur . execute ( " SELECT name, value FROM facts WHERE %s ORDER BY popularity DESC " % qterms , values )
res = cur . fetchall ( )
for r in res :
ret . append ( get_factoid_label ( r [ 0 ] , r [ 1 ] ) )
2008-08-04 10:11:15 +00:00
if not ret :
2021-07-24 18:34:04 +00:00
return " No matches found "
2018-05-21 22:45:04 +00:00
return ' Found: %s ' % ' , ' . join ( ret )
2007-02-04 17:35:40 +00:00
2008-08-07 11:37:25 +00:00
def sync ( self , irc , msg , args , channel ) :
""" [<channel>]
2008-05-05 11:34:57 +00:00
Downloads a copy of the database from the remote server .
2008-10-22 17:31:40 +00:00
Set the server with the channel variable supybot . plugins . Encyclopedia . remotedb .
2018-05-21 22:45:04 +00:00
If < channel > is not set , it will default to the channel
the command is given in or the global value .
2008-05-05 11:34:57 +00:00
"""
if not capab ( msg . prefix , " owner " ) :
2018-05-21 22:45:04 +00:00
irc . errorNoCapability ( " owner " )
2008-05-05 11:34:57 +00:00
return
2008-10-22 17:31:40 +00:00
remotedb = self . registryValue ( ' remotedb ' , channel )
if not remotedb :
return
2018-05-21 22:45:04 +00:00
2008-05-05 11:34:57 +00:00
def download_database ( location , dpath ) :
""" Download the database located at location to path dpath """
2008-08-07 11:37:25 +00:00
tmp_db = " %s %s tmp " % ( dpath , os . extsep )
2018-02-22 10:45:04 +00:00
data = utils . web . getUrl ( location )
fd = open ( tmp_db , ' w ' )
fd . write ( data ) # Download to a temporary file
2008-05-05 11:34:57 +00:00
fd . close ( )
2008-08-07 11:37:25 +00:00
# Do some checking to make sure we have an SQLite database
2018-02-22 10:45:04 +00:00
fd = open ( tmp_db , ' rb ' )
check = fd . read ( 15 )
2018-03-09 18:56:04 +00:00
if check == ' SQLite format 3 ' : # OK, rename to dpath
2008-08-07 11:37:25 +00:00
os . rename ( tmp_db , dpath )
2008-10-28 08:09:48 +00:00
try :
2018-03-09 18:56:04 +00:00
self . databases [ channel ] . db . close ( )
2009-10-12 18:26:35 +00:00
except :
pass
try :
2008-10-28 08:09:48 +00:00
self . databases . pop ( channel )
except :
pass
2018-02-22 10:45:04 +00:00
else : # Remove the temporary file and raise an error
2008-08-07 11:37:25 +00:00
os . remove ( tmp_db )
2018-02-22 10:45:04 +00:00
raise RuntimeError ( " Downloaded file was not an SQLite 3 database " )
2008-08-07 11:37:25 +00:00
db = self . registryValue ( ' database ' , channel )
if not db :
if channel :
irc . error ( " I don ' t have a database set for %s " % channel )
return
irc . error ( " There is no global database set, use ' config supybot.plugins.Encyclopedia.database <database> ' to set it " )
return
2008-10-22 17:31:40 +00:00
if not remotedb :
2008-08-07 11:37:25 +00:00
if channel :
irc . error ( " I don ' t have a remote database set for %s " % channel )
return
2008-10-22 17:31:40 +00:00
irc . error ( " There is no global remote database set, use ' config supybot.plugins.Encyclopedia.remotedb <url> ' to set it " )
2008-08-07 11:37:25 +00:00
return
2008-05-05 11:34:57 +00:00
dbpath = os . path . join ( self . registryValue ( ' datadir ' ) , ' %s .db ' % db )
2018-05-21 22:45:04 +00:00
# We're moving files and downloading, lots can go wrong so use lots of try blocks
2008-05-05 11:34:57 +00:00
try :
2018-05-21 22:45:04 +00:00
os . rename ( dbpath , " %s .bak " % dbpath )
2010-05-17 13:46:35 +00:00
except OSError :
2018-05-21 22:45:04 +00:00
# File doesn't exist yet, so nothing to back up
2010-05-17 13:46:35 +00:00
pass
2018-02-22 10:45:04 +00:00
except Exception as e :
2018-05-21 22:45:04 +00:00
self . log . error ( " Encyclopedia: Could not rename %s to %s .bak " % ( dbpath , dbpath ) )
self . log . error ( ' Encyclopedia: %s ' % utils . exnToString ( e ) )
2008-05-05 11:34:57 +00:00
irc . error ( " Internal error, see log " )
return
try :
# Downloading can take some time, let the user know we're doing something
2018-05-21 22:45:04 +00:00
irc . reply ( " Attemting to download database " )
2008-10-22 17:31:40 +00:00
download_database ( remotedb , dbpath )
2008-05-05 11:34:57 +00:00
irc . replySuccess ( )
2018-02-22 10:45:04 +00:00
except Exception as e :
2010-05-24 13:38:06 +00:00
self . log . error ( " Encyclopedia: Could not download %s to %s " % ( remotedb , dbpath ) )
2018-05-21 22:45:04 +00:00
self . log . error ( ' Encyclopedia: %s ' % utils . exnToString ( e ) )
2008-05-05 11:34:57 +00:00
irc . error ( " Internal error, see log " )
2018-05-21 22:45:04 +00:00
os . rename ( " %s .bak " % dbpath , dbpath )
2008-05-05 11:34:57 +00:00
return
2018-05-21 22:45:04 +00:00
sync = wrap ( sync , [ optional ( " channel " ) ] )
2008-10-22 17:31:40 +00:00
2021-07-24 18:34:04 +00:00
def lookup ( self , irc , msg , args , channel , author ) :
""" [<channel>] [<author>]
2009-10-12 18:26:35 +00:00
2021-07-24 18:34:04 +00:00
Lists factoids created or edited by < author > ,
2009-10-12 18:26:35 +00:00
< author > defaults to you .
"""
if not capab ( msg . prefix , " editfactoids " ) :
2018-05-21 22:45:04 +00:00
irc . errorNoCapability ( " editfactoids " )
2009-10-12 18:26:35 +00:00
return
if not author :
author = msg . prefix
2018-05-21 22:45:04 +00:00
if ' ! ' in author :
author = author [ : author . find ( ' ! ' ) ]
2009-10-12 18:26:35 +00:00
db = self . get_db ( channel )
cur = db . cursor ( )
2018-03-22 20:34:04 +00:00
auth_ret , edit_ret = [ ] , [ ]
2018-05-21 22:45:04 +00:00
cur . execute ( " SELECT name, value FROM facts WHERE author LIKE ? ORDER BY popularity DESC " , ( ' %s %% ' % author , ) )
auth_res = cur . fetchall ( )
2018-03-22 20:34:04 +00:00
cur . execute ( " SELECT name, value FROM facts WHERE editor LIKE ? AND author NOT LIKE ? ORDER BY popularity DESC " , ( ' %s %% ' % author , ' %s %% ' % author ) )
edit_res = cur . fetchall ( )
2009-10-12 18:26:35 +00:00
2018-05-21 22:45:04 +00:00
for r in auth_res :
2018-03-22 20:34:04 +00:00
auth_ret . append ( get_factoid_label ( r [ 0 ] , r [ 1 ] ) )
for r in edit_res :
edit_ret . append ( get_factoid_label ( r [ 0 ] , r [ 1 ] ) )
2018-05-21 22:45:04 +00:00
if not auth_ret :
2021-07-24 18:34:04 +00:00
auth_rmsg = " Authored: (none) "
2009-10-12 18:26:35 +00:00
else :
2018-05-21 22:45:04 +00:00
auth_rmsg = ' Authored: %s ' % ' , ' . join ( auth_ret )
if not edit_ret :
2021-07-24 18:34:04 +00:00
edit_rmsg = " Edited: (none) "
2009-10-12 18:26:35 +00:00
else :
2018-05-21 22:45:04 +00:00
edit_rmsg = ' Edited: %s ' % ' , ' . join ( edit_ret )
2021-07-23 19:13:04 +00:00
irc . reply ( " %s ; %s " % ( auth_rmsg , edit_rmsg ) )
2021-07-24 18:34:04 +00:00
lookup = wrap ( lookup , [ optional ( " channel " ) , optional ( " nick " ) ] )
def review ( self , irc , msg , args , channel , reqid ) :
""" [<channel>] [<id>]
Lists edit requests . If < id > is given , prints the suggested new value .
"""
if not capab ( msg . prefix , " editfactoids " ) :
irc . errorNoCapability ( " editfactoids " )
return
db = self . get_db ( channel )
cur = db . cursor ( )
if not reqid :
cur . execute ( " SELECT id, type, name, requester, requested FROM requests ORDER BY id DESC LIMIT 15 " )
res = cur . fetchall ( )
ret = [ ]
for r in res :
ret . append ( " [ %s ] %s ( %s ) by %s on %s " % ( r [ 0 ] , r [ 2 ] , r [ 1 ] , r [ 3 ] [ : r [ 3 ] . find ( ' ! ' ) ] , r [ 4 ] [ : r [ 4 ] . rfind ( ' . ' ) ] ) )
if not ret :
rmsg = " No requests found "
else :
rmsg = ' , ' . join ( ret )
else :
cur . execute ( " SELECT value FROM requests WHERE id = ? " , ( reqid , ) )
res = cur . fetchall ( )
if not res :
rmsg = " Request not found "
else :
rmsg = res [ 0 ] [ 0 ]
irc . reply ( rmsg )
review = wrap ( review , [ optional ( " channel " ) , optional ( " positiveInt " ) ] )
2018-05-21 22:45:04 +00:00
class ignore ( callbacks . Commands ) :
2021-07-24 18:34:04 +00:00
def add ( self , irc , msg , args , channel , banmask , expires ) :
""" [<channel>] <hostmask|nick> [<expires>]
2018-05-21 22:45:04 +00:00
Ignores commands / requests from < hostmask > or < nick > . If < expires > is
given , the ignore will expire after that ammount of seconds .
If < channel > is given , the ignore will only apply in that channel .
"""
if not capab ( msg . prefix , " editfactoids " ) :
irc . errorNoCapability ( " editfactoids " )
return
if channel :
c = ircdb . channels . getChannel ( channel )
c . addIgnore ( banmask , expires )
2009-10-12 18:26:35 +00:00
ircdb . channels . setChannel ( channel , c )
irc . replySuccess ( )
2018-05-21 22:45:04 +00:00
else :
ircdb . ignores . add ( banmask , expires )
2009-10-12 18:26:35 +00:00
irc . replySuccess ( )
2021-07-24 18:34:04 +00:00
add = wrap ( add , [ optional ( " channel " ) , ' hostmask ' , optional ( " expiry " , 0 ) ] )
2009-10-12 18:26:35 +00:00
2021-07-24 18:34:04 +00:00
def remove ( self , irc , msg , args , channel , banmask ) :
""" [<channel>] <hostmask|nick>
2009-10-12 18:26:35 +00:00
2018-05-21 22:45:04 +00:00
Removes an ignore previously set by @ignore. If < channel > was
given in the original @ignore command , it must be given here .
"""
if not capab ( msg . prefix , " editfactoids " ) :
irc . errorNoCapability ( " editfactoids " )
return
if channel :
c = ircdb . channels . getChannel ( channel )
try :
c . removeIgnore ( banmask )
ircdb . channels . setChannel ( channel , c )
irc . replySuccess ( )
except KeyError :
irc . error ( " That hostmask is not ignored in %s " % channel )
else :
try :
ircdb . ignores . remove ( banmask )
irc . replySuccess ( )
except KeyError :
irc . error ( " That hostmask is not globally ignored " )
2021-07-24 18:34:04 +00:00
remove = wrap ( remove , [ optional ( " channel " ) , ' hostmask ' ] )
2009-10-12 18:26:35 +00:00
2018-05-21 22:45:04 +00:00
def ignores ( self , irc , msg , args , channel ) :
""" [<channel>]
Lists all ignores set by @ignore. If < channel > is given ,
this will only list ignores set in that channel .
2009-10-12 18:26:35 +00:00
"""
if not capab ( msg . prefix , " editfactoids " ) :
irc . errorNoCapability ( " editfactoids " )
return
if channel :
c = ircdb . channels . getChannel ( channel )
2018-05-21 22:45:04 +00:00
if not c . ignores :
irc . reply ( " I ' m not currently ignoring anyone in %s " % channel )
2009-10-12 18:26:35 +00:00
else :
L = sorted ( c . ignores )
2018-02-22 10:45:04 +00:00
irc . reply ( utils . str . commaAndify ( list ( map ( repr , L ) ) ) )
2009-10-12 18:26:35 +00:00
else :
if ircdb . ignores . hostmasks :
2018-05-21 22:45:04 +00:00
irc . reply ( format ( ' % L ' , ( list ( map ( repr , ircdb . ignores . hostmasks ) ) ) ) )
2009-10-12 18:26:35 +00:00
else :
2018-05-21 22:45:04 +00:00
irc . reply ( " I ' m not currently globally ignoring anyone " )
ignores = wrap ( ignores , [ optional ( " channel " ) ] )
2009-10-12 18:26:35 +00:00
2007-02-04 17:35:40 +00:00
Class = Encyclopedia