1#! /usr/bin/env python3
- 2# -*- coding: utf-8 -*-
- 3
- 4"""
- 5Module for the common base class for all Bots
- 6"""
- 7
- 8importre
- 9
-10classBot():
-11"""Base class for things common between different protocols"""
-12def__init__(self):
-13self.CONFIG={}
-14self.ACTIONS=[]
-15self.GENERAL_ACTIONS=[]
-16
-17defgetConfig(self):
-18"""Return the current configuration"""
-19returnself.CONFIG
-20
-21defsetConfig(self,config):
-22"""Set the current configuration"""
-23self.CONFIG=config
-24
-25defregisterActions(self,actions):
-26"""Register actions to use"""
-27print("Adding actions:")
-28foractioninactions:
-29print(" - "+action.__name__)
-30self.ACTIONS.extend(actions)
-31
-32defregisterGeneralActions(self,actions):
-33"""Register general actions to use"""
-34print("Adding general actions:")
-35foractioninactions:
-36print(" - "+action.__name__)
-37self.GENERAL_ACTIONS.extend(actions)
-38
-39@staticmethod
-40deftokenize(message):
-41"""Split a message into normalized tokens"""
-42returnre.sub("[,.?:]"," ",message).strip().lower().split()
-
-
-
-
-
-
-
-
- class
- Bot:
-
-
-
-
-
-
11classBot():
-12"""Base class for things common between different protocols"""
-13def__init__(self):
-14self.CONFIG={}
-15self.ACTIONS=[]
-16self.GENERAL_ACTIONS=[]
-17
-18defgetConfig(self):
-19"""Return the current configuration"""
-20returnself.CONFIG
-21
-22defsetConfig(self,config):
-23"""Set the current configuration"""
-24self.CONFIG=config
-25
-26defregisterActions(self,actions):
-27"""Register actions to use"""
-28print("Adding actions:")
-29foractioninactions:
-30print(" - "+action.__name__)
-31self.ACTIONS.extend(actions)
-32
-33defregisterGeneralActions(self,actions):
-34"""Register general actions to use"""
-35print("Adding general actions:")
-36foractioninactions:
-37print(" - "+action.__name__)
-38self.GENERAL_ACTIONS.extend(actions)
-39
-40@staticmethod
-41deftokenize(message):
-42"""Split a message into normalized tokens"""
-43returnre.sub("[,.?:]"," ",message).strip().lower().split()
-
-
-
-
Base class for things common between different protocols
-
-
-
-
-
- CONFIG
-
-
-
-
-
-
-
-
-
-
- ACTIONS
-
-
-
-
-
-
-
-
-
-
- GENERAL_ACTIONS
-
-
-
-
-
-
-
-
-
-
-
-
- def
- getConfig(self):
-
-
-
-
-
-
18defgetConfig(self):
-19"""Return the current configuration"""
-20returnself.CONFIG
-
-
-
-
Return the current configuration
-
-
-
-
-
-
-
-
- def
- setConfig(self, config):
-
-
-
-
-
-
22defsetConfig(self,config):
-23"""Set the current configuration"""
-24self.CONFIG=config
-
33defregisterGeneralActions(self,actions):
-34"""Register general actions to use"""
-35print("Adding general actions:")
-36foractioninactions:
-37print(" - "+action.__name__)
-38self.GENERAL_ACTIONS.extend(actions)
-
-
-
-
Register general actions to use
-
-
-
-
-
-
-
-
@staticmethod
-
- def
- tokenize(message):
-
-
-
-
-
-
40@staticmethod
-41deftokenize(message):
-42"""Split a message into normalized tokens"""
-43returnre.sub("[,.?:]"," ",message).strip().lower().split()
-
-
-
-
Split a message into normalized tokens
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/docs/pdoc/discord_bot.html b/docs/pdoc/discord_bot.html
deleted file mode 100644
index 5a08113..0000000
--- a/docs/pdoc/discord_bot.html
+++ /dev/null
@@ -1,548 +0,0 @@
-
-
-
-
-
-
- discord_bot API documentation
-
-
-
-
-
-
-
-
-
-
-
-
-discord_bot
-
-
Module for the Discord bot.
-
-
Connecting, sending and receiving messages and doing custom actions.
-
-
-
-
-
-
-
1#! /usr/bin/env python3
- 2# -*- coding: utf-8 -*-
- 3
- 4"""
- 5Module for the Discord bot.
- 6
- 7Connecting, sending and receiving messages and doing custom actions.
- 8"""
- 9
-10importdiscord
-11
-12frombotimportBot
-13
-14classDiscordBot(discord.Client,Bot):
-15"""Bot implementing the discord protocol"""
-16def__init__(self):
-17Bot.__init__(self)
-18self.CONFIG={
-19"token":""
-20}
-21intents=discord.Intents.default()
-22intents.message_content=True
-23discord.Client.__init__(self,intents=intents)
-24
-25defbegin(self):
-26"""Start the bot"""
-27self.run(self.CONFIG.get("token"))
-28
-29asyncdefcheckMarvinActions(self,message):
-30"""Check if Marvin should perform any actions"""
-31words=self.tokenize(message.content)
-32ifself.user.name.lower()inwords:
-33foractioninself.ACTIONS:
-34response=action(words)
-35ifresponse:
-36awaitmessage.channel.send(response)
-37else:
-38foractioninself.GENERAL_ACTIONS:
-39response=action(words)
-40ifresponse:
-41awaitmessage.channel.send(response)
-42
-43asyncdefon_message(self,message):
-44"""Hook run on every message"""
-45print(f"#{message.channel.name} <{message.author}> {message.content}")
-46ifmessage.author.name==self.user.name:
-47# don't react to own messages
-48return
-49awaitself.checkMarvinActions(message)
-
-
-
-
-
-
-
-
- class
- DiscordBot(discord.client.Client, bot.Bot):
-
-
-
-
-
-
15classDiscordBot(discord.Client,Bot):
-16"""Bot implementing the discord protocol"""
-17def__init__(self):
-18Bot.__init__(self)
-19self.CONFIG={
-20"token":""
-21}
-22intents=discord.Intents.default()
-23intents.message_content=True
-24discord.Client.__init__(self,intents=intents)
-25
-26defbegin(self):
-27"""Start the bot"""
-28self.run(self.CONFIG.get("token"))
-29
-30asyncdefcheckMarvinActions(self,message):
-31"""Check if Marvin should perform any actions"""
-32words=self.tokenize(message.content)
-33ifself.user.name.lower()inwords:
-34foractioninself.ACTIONS:
-35response=action(words)
-36ifresponse:
-37awaitmessage.channel.send(response)
-38else:
-39foractioninself.GENERAL_ACTIONS:
-40response=action(words)
-41ifresponse:
-42awaitmessage.channel.send(response)
-43
-44asyncdefon_message(self,message):
-45"""Hook run on every message"""
-46print(f"#{message.channel.name} <{message.author}> {message.content}")
-47ifmessage.author.name==self.user.name:
-48# don't react to own messages
-49return
-50awaitself.checkMarvinActions(message)
-
-
-
-
Bot implementing the discord protocol
-
-
-
-
-
- CONFIG
-
-
-
-
-
-
-
-
-
-
-
-
- def
- begin(self):
-
-
-
-
-
-
26defbegin(self):
-27"""Start the bot"""
-28self.run(self.CONFIG.get("token"))
-
44asyncdefon_message(self,message):
-45"""Hook run on every message"""
-46print(f"#{message.channel.name} <{message.author}> {message.content}")
-47ifmessage.author.name==self.user.name:
-48# don't react to own messages
-49return
-50awaitself.checkMarvinActions(message)
-
24classIrcBot(Bot):
- 25"""Bot implementing the IRC protocol"""
- 26def__init__(self):
- 27super().__init__()
- 28self.CONFIG={
- 29"server":None,
- 30"port":6667,
- 31"channel":None,
- 32"nick":"marvin",
- 33"realname":"Marvin The All Mighty dbwebb-bot",
- 34"ident":None,
- 35"irclogfile":"irclog.txt",
- 36"irclogmax":20,
- 37"dirIncoming":"incoming",
- 38"dirDone":"done",
- 39"lastfm":None,
- 40}
- 41
- 42# Socket for IRC server
- 43self.SOCKET=None
- 44
- 45# Keep a log of the latest messages
- 46self.IRCLOG=None
- 47
- 48
- 49defconnectToServer(self):
- 50"""Connect to the IRC Server"""
- 51
- 52# Create the socket & Connect to the server
- 53server=self.CONFIG["server"]
- 54port=self.CONFIG["port"]
- 55
- 56ifserverandport:
- 57self.SOCKET=socket.socket()
- 58print("Connecting: {SERVER}:{PORT}".format(SERVER=server,PORT=port))
- 59self.SOCKET.connect((server,port))
- 60else:
- 61print("Failed to connect, missing server or port in configuration.")
- 62return
- 63
- 64# Send the nick to server
- 65nick=self.CONFIG["nick"]
- 66ifnick:
- 67msg='NICK {NICK}\r\n'.format(NICK=nick)
- 68self.sendMsg(msg)
- 69else:
- 70print("Ignore sending nick, missing nick in configuration.")
- 71
- 72# Present yourself
- 73realname=self.CONFIG["realname"]
- 74self.sendMsg('USER {NICK} 0 * :{REALNAME}\r\n'.format(NICK=nick,REALNAME=realname))
- 75
- 76# This is my nick, i promise!
- 77ident=self.CONFIG["ident"]
- 78ifident:
- 79self.sendMsg('PRIVMSG nick IDENTIFY {IDENT}\r\n'.format(IDENT=ident))
- 80else:
- 81print("Ignore identifying with password, ident is not set.")
- 82
- 83# Join a channel
- 84channel=self.CONFIG["channel"]
- 85ifchannel:
- 86self.sendMsg('JOIN {CHANNEL}\r\n'.format(CHANNEL=channel))
- 87else:
- 88print("Ignore joining channel, missing channel name in configuration.")
- 89
- 90defsendPrivMsg(self,message,channel):
- 91"""Send and log a PRIV message"""
- 92ifchannel==self.CONFIG["channel"]:
- 93self.ircLogAppend(user=self.CONFIG["nick"].ljust(8),message=message)
- 94
- 95msg="PRIVMSG {CHANNEL} :{MSG}\r\n".format(CHANNEL=channel,MSG=message)
- 96self.sendMsg(msg)
- 97
- 98defsendMsg(self,msg):
- 99"""Send and occasionally print the message sent"""
-100print("SEND: "+msg.rstrip('\r\n'))
-101self.SOCKET.send(msg.encode())
-102
-103defdecode_irc(self,raw,preferred_encs=None):
-104"""
-105 Do character detection.
-106 You can send preferred encodings as a list through preferred_encs.
-107 http://stackoverflow.com/questions/938870/python-irc-bot-and-encoding-issue
-108 """
-109ifpreferred_encsisNone:
-110preferred_encs=["UTF-8","CP1252","ISO-8859-1"]
-111
-112changed=False
-113enc=None
-114forencinpreferred_encs:
-115try:
-116res=raw.decode(enc)
-117changed=True
-118break
-119exceptException:
-120pass
-121
-122ifnotchanged:
-123try:
-124enc=chardet.detect(raw)['encoding']
-125res=raw.decode(enc)
-126exceptException:
-127res=raw.decode(enc,'ignore')
-128
-129returnres
-130
-131defreceive(self):
-132"""Read incoming message and guess encoding"""
-133try:
-134buf=self.SOCKET.recv(2048)
-135lines=self.decode_irc(buf)
-136lines=lines.split("\n")
-137buf=lines.pop()
-138exceptExceptionaserr:
-139print("Error reading incoming message. "+err)
-140
-141returnlines
-142
-143defircLogAppend(self,line=None,user=None,message=None):
-144"""Read incoming message and guess encoding"""
-145ifnotuser:
-146user=re.search(r"(?<=:)\w+",line[0]).group(0)
-147
-148ifnotmessage:
-149message=' '.join(line[3:]).lstrip(':')
-150
-151self.IRCLOG.append({
-152'time':datetime.now().strftime("%H:%M").rjust(5),
-153'user':user,
-154'msg':message
-155})
-156
-157defircLogWriteToFile(self):
-158"""Write IRClog to file"""
-159withopen(self.CONFIG["irclogfile"],'w',encoding="UTF-8")asf:
-160json.dump(list(self.IRCLOG),f,indent=2)
-161
-162defreadincoming(self):
-163"""
-164 Read all files in the directory incoming, send them as a message if
-165 they exists and then move the file to directory done.
-166 """
-167ifnotos.path.isdir(self.CONFIG["dirIncoming"]):
-168return
-169
-170listing=os.listdir(self.CONFIG["dirIncoming"])
-171
-172forinfileinlisting:
-173filename=os.path.join(self.CONFIG["dirIncoming"],infile)
-174
-175withopen(filename,"r",encoding="UTF-8")asf:
-176formsginf:
-177self.sendPrivMsg(msg,self.CONFIG["channel"])
-178
-179try:
-180shutil.move(filename,self.CONFIG["dirDone"])
-181exceptException:
-182os.remove(filename)
-183
-184defmainLoop(self):
-185"""For ever, listen and answer to incoming chats"""
-186self.IRCLOG=deque([],self.CONFIG["irclogmax"])
-187
-188while1:
-189# Write irclog
-190self.ircLogWriteToFile()
-191
-192# Check in any in the incoming directory
-193self.readincoming()
-194
-195forlineinself.receive():
-196print(line)
-197words=line.strip().split()
-198
-199ifnotwords:
-200continue
-201
-202self.checkIrcActions(words)
-203self.checkMarvinActions(words)
-204
-205defbegin(self):
-206"""Start the bot"""
-207self.connectToServer()
-208self.mainLoop()
-209
-210defcheckIrcActions(self,words):
-211"""
-212 Check if Marvin should take action on any messages defined in the
-213 IRC protocol.
-214 """
-215ifwords[0]=="PING":
-216self.sendMsg("PONG {ARG}\r\n".format(ARG=words[1]))
-217
-218ifwords[1]=='INVITE':
-219self.sendMsg('JOIN {CHANNEL}\r\n'.format(CHANNEL=words[3]))
-220
-221defcheckMarvinActions(self,words):
-222"""Check if Marvin should perform any actions"""
-223ifwords[1]=='PRIVMSG'andwords[2]==self.CONFIG["channel"]:
-224self.ircLogAppend(words)
-225
-226ifwords[1]=='PRIVMSG':
-227raw=' '.join(words[3:])
-228row=self.tokenize(raw)
-229
-230ifself.CONFIG["nick"]inrow:
-231foractioninself.ACTIONS:
-232msg=action(row)
-233ifmsg:
-234self.sendPrivMsg(msg,words[2])
-235break
-236else:
-237foractioninself.GENERAL_ACTIONS:
-238msg=action(row)
-239ifmsg:
-240self.sendPrivMsg(msg,words[2])
-241break
-
-
-
-
Bot implementing the IRC protocol
-
-
-
-
-
- CONFIG
-
-
-
-
-
-
-
-
-
-
- SOCKET
-
-
-
-
-
-
-
-
-
-
- IRCLOG
-
-
-
-
-
-
-
-
-
-
-
-
- def
- connectToServer(self):
-
-
-
-
-
-
49defconnectToServer(self):
-50"""Connect to the IRC Server"""
-51
-52# Create the socket & Connect to the server
-53server=self.CONFIG["server"]
-54port=self.CONFIG["port"]
-55
-56ifserverandport:
-57self.SOCKET=socket.socket()
-58print("Connecting: {SERVER}:{PORT}".format(SERVER=server,PORT=port))
-59self.SOCKET.connect((server,port))
-60else:
-61print("Failed to connect, missing server or port in configuration.")
-62return
-63
-64# Send the nick to server
-65nick=self.CONFIG["nick"]
-66ifnick:
-67msg='NICK {NICK}\r\n'.format(NICK=nick)
-68self.sendMsg(msg)
-69else:
-70print("Ignore sending nick, missing nick in configuration.")
-71
-72# Present yourself
-73realname=self.CONFIG["realname"]
-74self.sendMsg('USER {NICK} 0 * :{REALNAME}\r\n'.format(NICK=nick,REALNAME=realname))
-75
-76# This is my nick, i promise!
-77ident=self.CONFIG["ident"]
-78ifident:
-79self.sendMsg('PRIVMSG nick IDENTIFY {IDENT}\r\n'.format(IDENT=ident))
-80else:
-81print("Ignore identifying with password, ident is not set.")
-82
-83# Join a channel
-84channel=self.CONFIG["channel"]
-85ifchannel:
-86self.sendMsg('JOIN {CHANNEL}\r\n'.format(CHANNEL=channel))
-87else:
-88print("Ignore joining channel, missing channel name in configuration.")
-
157defircLogWriteToFile(self):
-158"""Write IRClog to file"""
-159withopen(self.CONFIG["irclogfile"],'w',encoding="UTF-8")asf:
-160json.dump(list(self.IRCLOG),f,indent=2)
-
-
-
-
Write IRClog to file
-
-
-
-
-
-
-
-
- def
- readincoming(self):
-
-
-
-
-
-
162defreadincoming(self):
-163"""
-164 Read all files in the directory incoming, send them as a message if
-165 they exists and then move the file to directory done.
-166 """
-167ifnotos.path.isdir(self.CONFIG["dirIncoming"]):
-168return
-169
-170listing=os.listdir(self.CONFIG["dirIncoming"])
-171
-172forinfileinlisting:
-173filename=os.path.join(self.CONFIG["dirIncoming"],infile)
-174
-175withopen(filename,"r",encoding="UTF-8")asf:
-176formsginf:
-177self.sendPrivMsg(msg,self.CONFIG["channel"])
-178
-179try:
-180shutil.move(filename,self.CONFIG["dirDone"])
-181exceptException:
-182os.remove(filename)
-
-
-
-
Read all files in the directory incoming, send them as a message if
-they exists and then move the file to directory done.
-
-
-
-
-
-
-
-
- def
- mainLoop(self):
-
-
-
-
-
-
184defmainLoop(self):
-185"""For ever, listen and answer to incoming chats"""
-186self.IRCLOG=deque([],self.CONFIG["irclogmax"])
-187
-188while1:
-189# Write irclog
-190self.ircLogWriteToFile()
-191
-192# Check in any in the incoming directory
-193self.readincoming()
-194
-195forlineinself.receive():
-196print(line)
-197words=line.strip().split()
-198
-199ifnotwords:
-200continue
-201
-202self.checkIrcActions(words)
-203self.checkMarvinActions(words)
-
-
-
-
For ever, listen and answer to incoming chats
-
-
-
-
-
-
-
-
- def
- begin(self):
-
-
-
-
-
-
205defbegin(self):
-206"""Start the bot"""
-207self.connectToServer()
-208self.mainLoop()
-
-
-
-
Start the bot
-
-
-
-
-
-
-
-
- def
- checkIrcActions(self, words):
-
-
-
-
-
-
210defcheckIrcActions(self,words):
-211"""
-212 Check if Marvin should take action on any messages defined in the
-213 IRC protocol.
-214 """
-215ifwords[0]=="PING":
-216self.sendMsg("PONG {ARG}\r\n".format(ARG=words[1]))
-217
-218ifwords[1]=='INVITE':
-219self.sendMsg('JOIN {CHANNEL}\r\n'.format(CHANNEL=words[3]))
-
-
-
-
Check if Marvin should take action on any messages defined in the
-IRC protocol.
-
-
-
-
\ No newline at end of file
diff --git a/docs/pdoc/main.html b/docs/pdoc/main.html
deleted file mode 100644
index 50f44c4..0000000
--- a/docs/pdoc/main.html
+++ /dev/null
@@ -1,745 +0,0 @@
-
-
-
-
-
-
- main API documentation
-
-
-
-
-
-
-
-
-
-
-
-
-main
-
-
An IRC bot that answers random questions, keeps a log from the IRC-chat,
-easy to integrate in a webpage and montores a phpBB forum for latest topics
-by loggin in to the forum and checking the RSS-feed.
Check out the file 'marvin_config_default.json' on how to configure, instead
-of using cli-options. The default configfile is 'marvin_config.json' but you
-can change that using cli-options.
-
-
Make own actions
-
-
Check the file 'marvin_strings.json' for the file where most of the strings
-are defined and check out 'marvin_actions.py' to see how to write your own
-actions. Its just a small function.
-
-
Read from incoming
-
-
Marvin reads messages from the incoming/ directory, if it exists, and writes
-it out the the irc channel.
-
-
-
-
-
-
-
1#! /usr/bin/env python3
- 2# -*- coding: utf-8 -*-
- 3
- 4"""
- 5An IRC bot that answers random questions, keeps a log from the IRC-chat,
- 6easy to integrate in a webpage and montores a phpBB forum for latest topics
- 7by loggin in to the forum and checking the RSS-feed.
- 8
- 9You need to install additional modules.
- 10
- 11# Install needed modules in local directory
- 12pip3 install --target modules/ feedparser beautifulsoup4 chardet
- 13
- 14Modules in modules/ will be loaded automatically. If you want to use a
- 15different directory you can start the program like this instead:
- 16
- 17PYTHONPATH=modules python3 main.py
- 18
- 19# To get help
- 20PYTHONPATH=modules python3 main.py --help
- 21
- 22# Example of using options
- 23--server=irc.bsnet.se --channel=#db-o-webb
- 24--server=irc.bsnet.se --port=6667 --channel=#db-o-webb
- 25--nick=marvin --ident=secret
- 26
- 27# Configuration
- 28Check out the file 'marvin_config_default.json' on how to configure, instead
- 29of using cli-options. The default configfile is 'marvin_config.json' but you
- 30can change that using cli-options.
- 31
- 32# Make own actions
- 33Check the file 'marvin_strings.json' for the file where most of the strings
- 34are defined and check out 'marvin_actions.py' to see how to write your own
- 35actions. Its just a small function.
- 36
- 37# Read from incoming
- 38Marvin reads messages from the incoming/ directory, if it exists, and writes
- 39it out the the irc channel.
- 40"""
- 41
- 42importargparse
- 43importjson
- 44importos
- 45importsys
- 46
- 47fromdiscord_botimportDiscordBot
- 48fromirc_botimportIrcBot
- 49
- 50importmarvin_actions
- 51importmarvin_general_actions
- 52
- 53#
- 54# General stuff about this program
- 55#
- 56PROGRAM="marvin"
- 57AUTHOR="Mikael Roos"
- 58EMAIL="mikael.t.h.roos@gmail.com"
- 59VERSION="0.3.0"
- 60MSG_VERSION="{program} version {version}.".format(program=PROGRAM,version=VERSION)
- 61
- 62
- 63
- 64defprintVersion():
- 65"""
- 66 Print version information and exit.
- 67 """
- 68print(MSG_VERSION)
- 69sys.exit(0)
- 70
- 71
- 72defmergeOptionsWithConfigFile(options,configFile):
- 73"""
- 74 Read information from config file.
- 75 """
- 76ifos.path.isfile(configFile):
- 77withopen(configFile,encoding="UTF-8")asf:
- 78data=json.load(f)
- 79
- 80options.update(data)
- 81res=json.dumps(options,sort_keys=True,indent=4,separators=(',',': '))
- 82
- 83msg="Read configuration from config file '{file}'. Current configuration is:\n{config}"
- 84print(msg.format(config=res,file=configFile))
- 85
- 86else:
- 87print("Config file '{file}' is not readable, skipping.".format(file=configFile))
- 88
- 89returnoptions
- 90
- 91
- 92defparseOptions(options):
- 93"""
- 94 Merge default options with incoming options and arguments and return them as a dictionary.
- 95 """
- 96
- 97parser=argparse.ArgumentParser()
- 98parser.add_argument("protocol",choices=["irc","discord"],nargs="?",default="irc")
- 99parser.add_argument("-v","--version",action="store_true")
-100parser.add_argument("--config")
-101
-102forkey,valueinoptions.items():
-103parser.add_argument(f"--{key}",type=type(value))
-104
-105args=vars(parser.parse_args())
-106ifargs["version"]:
-107printVersion()
-108ifargs["config"]:
-109mergeOptionsWithConfigFile(options,args["config"])
-110
-111forparameterinoptions:
-112ifargs[parameter]:
-113options[parameter]=args[parameter]
-114
-115res=json.dumps(options,sort_keys=True,indent=4,separators=(',',': '))
-116print("Configuration updated after cli options:\n{config}".format(config=res))
-117
-118returnoptions
-119
-120
-121defdetermineProtocol():
-122"""Parse the argument to determine what protocol to use"""
-123parser=argparse.ArgumentParser()
-124parser.add_argument("protocol",choices=["irc","discord"],nargs="?",default="irc")
-125arg,_=parser.parse_known_args()
-126returnarg.protocol
-127
-128
-129defcreateBot(protocol):
-130"""Return an instance of a bot with the requested implementation"""
-131ifprotocol=="irc":
-132returnIrcBot()
-133ifprotocol=="discord":
-134returnDiscordBot()
-135raiseValueError(f"Unsupported protocol: {protocol}")
-136
-137
-138defmain():
-139"""
-140 Main function to carry out the work.
-141 """
-142protocol=determineProtocol()
-143bot=createBot(protocol)
-144options=bot.getConfig()
-145options.update(mergeOptionsWithConfigFile(options,"marvin_config.json"))
-146config=parseOptions(options)
-147bot.setConfig(config)
-148marvin_actions.setConfig(options)
-149marvin_general_actions.setConfig(options)
-150actions=marvin_actions.getAllActions()
-151general_actions=marvin_general_actions.getAllGeneralActions()
-152bot.registerActions(actions)
-153bot.registerGeneralActions(general_actions)
-154bot.begin()
-155
-156sys.exit(0)
-157
-158
-159if__name__=="__main__":
-160main()
-
-
-
-
-
-
- PROGRAM =
-'marvin'
-
-
-
-
-
-
-
-
-
-
- AUTHOR =
-'Mikael Roos'
-
-
-
-
-
-
-
-
-
-
- EMAIL =
-'mikael.t.h.roos@gmail.com'
-
-
-
-
-
-
-
-
-
-
- VERSION =
-'0.3.0'
-
-
-
-
-
-
-
-
-
-
- MSG_VERSION =
-'marvin version 0.3.0.'
-
-
-
-
-
-
-
-
-
-
-
-
- def
- printVersion():
-
-
-
-
-
-
65defprintVersion():
-66"""
-67 Print version information and exit.
-68 """
-69print(MSG_VERSION)
-70sys.exit(0)
-
73defmergeOptionsWithConfigFile(options,configFile):
-74"""
-75 Read information from config file.
-76 """
-77ifos.path.isfile(configFile):
-78withopen(configFile,encoding="UTF-8")asf:
-79data=json.load(f)
-80
-81options.update(data)
-82res=json.dumps(options,sort_keys=True,indent=4,separators=(',',': '))
-83
-84msg="Read configuration from config file '{file}'. Current configuration is:\n{config}"
-85print(msg.format(config=res,file=configFile))
-86
-87else:
-88print("Config file '{file}' is not readable, skipping.".format(file=configFile))
-89
-90returnoptions
-
-
-
-
Read information from config file.
-
-
-
-
-
-
-
-
- def
- parseOptions(options):
-
-
-
-
-
-
93defparseOptions(options):
- 94"""
- 95 Merge default options with incoming options and arguments and return them as a dictionary.
- 96 """
- 97
- 98parser=argparse.ArgumentParser()
- 99parser.add_argument("protocol",choices=["irc","discord"],nargs="?",default="irc")
-100parser.add_argument("-v","--version",action="store_true")
-101parser.add_argument("--config")
-102
-103forkey,valueinoptions.items():
-104parser.add_argument(f"--{key}",type=type(value))
-105
-106args=vars(parser.parse_args())
-107ifargs["version"]:
-108printVersion()
-109ifargs["config"]:
-110mergeOptionsWithConfigFile(options,args["config"])
-111
-112forparameterinoptions:
-113ifargs[parameter]:
-114options[parameter]=args[parameter]
-115
-116res=json.dumps(options,sort_keys=True,indent=4,separators=(',',': '))
-117print("Configuration updated after cli options:\n{config}".format(config=res))
-118
-119returnoptions
-
-
-
-
Merge default options with incoming options and arguments and return them as a dictionary.
-
-
-
-
-
-
-
-
- def
- determineProtocol():
-
-
-
-
-
-
122defdetermineProtocol():
-123"""Parse the argument to determine what protocol to use"""
-124parser=argparse.ArgumentParser()
-125parser.add_argument("protocol",choices=["irc","discord"],nargs="?",default="irc")
-126arg,_=parser.parse_known_args()
-127returnarg.protocol
-
-
-
-
Parse the argument to determine what protocol to use
-
-
-
-
-
-
-
-
- def
- createBot(protocol):
-
-
-
-
-
-
130defcreateBot(protocol):
-131"""Return an instance of a bot with the requested implementation"""
-132ifprotocol=="irc":
-133returnIrcBot()
-134ifprotocol=="discord":
-135returnDiscordBot()
-136raiseValueError(f"Unsupported protocol: {protocol}")
-
-
-
-
Return an instance of a bot with the requested implementation
-
-
-
-
-
-
-
-
- def
- main():
-
-
-
-
-
-
139defmain():
-140"""
-141 Main function to carry out the work.
-142 """
-143protocol=determineProtocol()
-144bot=createBot(protocol)
-145options=bot.getConfig()
-146options.update(mergeOptionsWithConfigFile(options,"marvin_config.json"))
-147config=parseOptions(options)
-148bot.setConfig(config)
-149marvin_actions.setConfig(options)
-150marvin_general_actions.setConfig(options)
-151actions=marvin_actions.getAllActions()
-152general_actions=marvin_general_actions.getAllGeneralActions()
-153bot.registerActions(actions)
-154bot.registerGeneralActions(general_actions)
-155bot.begin()
-156
-157sys.exit(0)
-
-
-
-
Main function to carry out the work.
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/docs/pdoc/marvin_actions.html b/docs/pdoc/marvin_actions.html
deleted file mode 100644
index 85fdf98..0000000
--- a/docs/pdoc/marvin_actions.html
+++ /dev/null
@@ -1,2115 +0,0 @@
-
-
-
-
-
-
- marvin_actions API documentation
-
-
-
-
-
-
-
-
-
-
-
-
-marvin_actions
-
-
Make actions for Marvin, one function for each action.
-
-
-
-
-
-
-
1#! /usr/bin/env python3
- 2# -*- coding: utf-8 -*-
- 3
- 4"""
- 5Make actions for Marvin, one function for each action.
- 6"""
- 7fromurllib.parseimportquote_plus
- 8fromurllib.requestimporturlopen
- 9importcalendar
- 10importdatetime
- 11importjson
- 12importrandom
- 13importrequests
- 14
- 15frombs4importBeautifulSoup
- 16
- 17
- 18defgetAllActions():
- 19"""
- 20 Return all actions in an array.
- 21 """
- 22return[
- 23marvinExplainShell,
- 24marvinGoogle,
- 25marvinLunch,
- 26marvinVideoOfToday,
- 27marvinWhoIs,
- 28marvinHelp,
- 29marvinSource,
- 30marvinBudord,
- 31marvinQuote,
- 32marvinStats,
- 33marvinIrcLog,
- 34marvinListen,
- 35marvinWeather,
- 36marvinSun,
- 37marvinSayHi,
- 38marvinSmile,
- 39marvinStrip,
- 40marvinTimeToBBQ,
- 41marvinBirthday,
- 42marvinNameday,
- 43marvinUptime,
- 44marvinStream,
- 45marvinPrinciple,
- 46marvinJoke,
- 47marvinCommit
- 48]
- 49
- 50
- 51# Load all strings from file
- 52withopen("marvin_strings.json",encoding="utf-8")asf:
- 53STRINGS=json.load(f)
- 54
- 55# Configuration loaded
- 56CONFIG=None
- 57
- 58defsetConfig(config):
- 59"""
- 60 Keep reference to the loaded configuration.
- 61 """
- 62globalCONFIG
- 63CONFIG=config
- 64
- 65
- 66defgetString(key,key1=None):
- 67"""
- 68 Get a string from the string database.
- 69 """
- 70data=STRINGS[key]
- 71ifisinstance(data,list):
- 72res=data[random.randint(0,len(data)-1)]
- 73elifisinstance(data,dict):
- 74ifkey1isNone:
- 75res=data
- 76else:
- 77res=data[key1]
- 78ifisinstance(res,list):
- 79res=res[random.randint(0,len(res)-1)]
- 80elifisinstance(data,str):
- 81res=data
- 82
- 83returnres
- 84
- 85
- 86defmarvinSmile(row):
- 87"""
- 88 Make Marvin smile.
- 89 """
- 90msg=None
- 91ifany(rinrowforrin["smile","le","skratta","smilies"]):
- 92smilie=getString("smile")
- 93msg="{SMILE}".format(SMILE=smilie)
- 94returnmsg
- 95
- 96
- 97defwordsAfterKeyWords(words,keyWords):
- 98"""
- 99 Return all items in the words list after the first occurence
-100 of an item in the keyWords list.
-101 """
-102kwIndex=[]
-103forkwinkeyWords:
-104ifkwinwords:
-105kwIndex.append(words.index(kw))
-106
-107ifnotkwIndex:
-108returnNone
-109
-110returnwords[min(kwIndex)+1:]
-111
-112
-113defmarvinGoogle(row):
-114"""
-115 Let Marvin present an url to google.
-116 """
-117query=wordsAfterKeyWords(row,["google","googla"])
-118ifnotquery:
-119returnNone
-120
-121searchStr=" ".join(query)
-122url="https://www.google.se/search?q="
-123url+=quote_plus(searchStr)
-124msg=getString("google")
-125returnmsg.format(url)
-126
-127
-128defmarvinExplainShell(row):
-129"""
-130 Let Marvin present an url to the service explain shell to
-131 explain a shell command.
-132 """
-133query=wordsAfterKeyWords(row,["explain","förklara"])
-134ifnotquery:
-135returnNone
-136cmd=" ".join(query)
-137url="http://explainshell.com/explain?cmd="
-138url+=quote_plus(cmd,"/:")
-139msg=getString("explainShell")
-140returnmsg.format(url)
-141
-142
-143defmarvinSource(row):
-144"""
-145 State message about sourcecode.
-146 """
-147msg=None
-148ifany(rinrowforrin["källkod","source"]):
-149msg=getString("source")
-150
-151returnmsg
-152
-153
-154defmarvinBudord(row):
-155"""
-156 What are the budord for Marvin?
-157 """
-158msg=None
-159ifany(rinrowforrin["budord","stentavla"]):
-160ifany(rinrowforrin["#1","1"]):
-161msg=getString("budord","#1")
-162elifany(rinrowforrin["#2","2"]):
-163msg=getString("budord","#2")
-164elifany(rinrowforrin["#3","3"]):
-165msg=getString("budord","#3")
-166elifany(rinrowforrin["#4","4"]):
-167msg=getString("budord","#4")
-168elifany(rinrowforrin["#5","5"]):
-169msg=getString("budord","#5")
-170
-171returnmsg
-172
-173
-174defmarvinQuote(row):
-175"""
-176 Make a quote.
-177 """
-178msg=None
-179ifany(rinrowforrin["quote","citat","filosofi","filosofera"]):
-180msg=getString("hitchhiker")
-181
-182returnmsg
-183
-184
-185defvideoOfToday():
-186"""
-187 Check what day it is and provide a url to a suitable video together with a greeting.
-188 """
-189dayNum=datetime.date.weekday(datetime.date.today())+1
-190msg=getString("weekdays",str(dayNum))
-191video=getString("video-of-today",str(dayNum))
-192
-193ifvideo:
-194msg+=" En passande video är "+video
-195else:
-196msg+=" Jag har ännu ingen passande video för denna dagen."
-197
-198returnmsg
-199
-200
-201defmarvinVideoOfToday(row):
-202"""
-203 Show the video of today.
-204 """
-205msg=None
-206ifany(rinrowforrin["idag","dagens"]):
-207ifany(rinrowforrin["video","youtube","tube"]):
-208msg=videoOfToday()
-209
-210returnmsg
-211
-212
-213defmarvinWhoIs(row):
-214"""
-215 Who is Marvin.
-216 """
-217msg=None
-218ifall(rinrowforrin["vem","är"]):
-219msg=getString("whois")
-220
-221returnmsg
-222
-223
-224defmarvinHelp(row):
-225"""
-226 Provide a menu.
-227 """
-228msg=None
-229ifany(rinrowforrin["hjälp","help","menu","meny"]):
-230msg=getString("menu")
-231
-232returnmsg
-233
-234
-235defmarvinStats(row):
-236"""
-237 Provide a link to the stats.
-238 """
-239msg=None
-240ifany(rinrowforrin["stats","statistik","ircstats"]):
-241msg=getString("ircstats")
-242
-243returnmsg
-244
-245
-246defmarvinIrcLog(row):
-247"""
-248 Provide a link to the irclog
-249 """
-250msg=None
-251ifany(rinrowforrin["irc","irclog","log","irclogg","logg","historik"]):
-252msg=getString("irclog")
-253
-254returnmsg
-255
-256
-257defmarvinSayHi(row):
-258"""
-259 Say hi with a nice message.
-260 """
-261msg=None
-262ifany(rinrowforrin[
-263"snälla","hej","tjena","morsning","morrn","mår","hallå",
-264"halloj","läget","snäll","duktig","träna","träning",
-265"utbildning","tack","tacka","tackar","tacksam"
-266]):
-267smile=getString("smile")
-268hello=getString("hello")
-269friendly=getString("friendly")
-270msg="{}{}{}".format(smile,hello,friendly)
-271
-272returnmsg
-273
-274
-275defmarvinLunch(row):
-276"""
-277 Help decide where to eat.
-278 """
-279lunchOptions={
-280'stan centrum karlskrona kna':'lunch-karlskrona',
-281'ängelholm angelholm engelholm':'lunch-angelholm',
-282'hässleholm hassleholm':'lunch-hassleholm',
-283'malmö malmo malmoe':'lunch-malmo',
-284'göteborg goteborg gbg':'lunch-goteborg'
-285}
-286
-287ifany(rinrowforrin["lunch","mat","äta","luncha"]):
-288lunchStr=getString('lunch-message')
-289
-290forkeys,valueinlunchOptions.items():
-291ifany(rinrowforrinkeys.split(" ")):
-292returnlunchStr.format(getString(value))
-293
-294returnlunchStr.format(getString('lunch-bth'))
-295
-296returnNone
-297
-298
-299defmarvinListen(row):
-300"""
-301 Return music last listened to.
-302 """
-303msg=None
-304ifany(rinrowforrin["lyssna","lyssnar","musik"]):
-305
-306ifnotCONFIG["lastfm"]:
-307returngetString("listen","disabled")
-308
-309url="http://ws.audioscrobbler.com/2.0/"
-310
-311try:
-312params=dict(
-313method="user.getrecenttracks",
-314user=CONFIG["lastfm"]["user"],
-315api_key=CONFIG["lastfm"]["apikey"],
-316format="json",
-317limit="1"
-318)
-319
-320resp=requests.get(url=url,params=params,timeout=5)
-321data=json.loads(resp.text)
-322
-323artist=data["recenttracks"]["track"][0]["artist"]["#text"]
-324title=data["recenttracks"]["track"][0]["name"]
-325link=data["recenttracks"]["track"][0]["url"]
-326
-327msg=getString("listen","success").format(artist=artist,title=title,link=link)
-328
-329exceptException:
-330msg=getString("listen","failed")
-331
-332returnmsg
-333
-334
-335defmarvinSun(row):
-336"""
-337 Check when the sun goes up and down.
-338 """
-339msg=None
-340ifany(rinrowforrin["sol","solen","solnedgång","soluppgång"]):
-341try:
-342soup=BeautifulSoup(urlopen('http://www.timeanddate.com/sun/sweden/jonkoping'))
-343spans=soup.find_all("span",{"class":"three"})
-344sunrise=spans[0].text
-345sunset=spans[1].text
-346msg=getString("sun").format(sunrise,sunset)
-347
-348exceptException:
-349msg=getString("sun-no")
-350
-351returnmsg
-352
-353
-354defmarvinWeather(row):
-355"""
-356 Check what the weather prognosis looks like.
-357 """
-358msg=None
-359ifany(rinrowforrin["väder","vädret","prognos","prognosen","smhi"]):
-360url=getString("smhi","url")
-361try:
-362soup=BeautifulSoup(urlopen(url))
-363msg="{}. {}. {}".format(
-364soup.h1.text,
-365soup.h4.text,
-366soup.h4.findNextSibling("p").text
-367)
-368
-369exceptException:
-370msg=getString("smhi","failed")
-371
-372returnmsg
-373
-374
-375defmarvinStrip(row):
-376"""
-377 Get a comic strip.
-378 """
-379msg=None
-380ifany(rinrowforrin["strip","comic","nöje","paus"]):
-381msg=commitStrip(randomize=any(rinrowforrin["rand","random","slump","lucky"]))
-382returnmsg
-383
-384
-385defcommitStrip(randomize=False):
-386"""
-387 Latest or random comic strip from CommitStrip.
-388 """
-389msg=getString("commitstrip","message")
-390
-391ifrandomize:
-392first=getString("commitstrip","first")
-393last=getString("commitstrip","last")
-394rand=random.randint(first,last)
-395url=getString("commitstrip","urlPage")+str(rand)
-396else:
-397url=getString("commitstrip","url")
-398
-399returnmsg.format(url=url)
-400
-401
-402defmarvinTimeToBBQ(row):
-403"""
-404 Calcuate the time to next barbecue and print a appropriate msg
-405 """
-406msg=None
-407ifany(rinrowforrin["grilla","grill","grillcon","bbq"]):
-408url=getString("barbecue","url")
-409nextDate=nextBBQ()
-410today=datetime.date.today()
-411daysRemaining=(nextDate-today).days
-412
-413ifdaysRemaining==0:
-414msg=getString("barbecue","today")
-415elifdaysRemaining==1:
-416msg=getString("barbecue","tomorrow")
-417elif1<daysRemaining<14:
-418msg=getString("barbecue","week")%nextDate
-419elif14<daysRemaining<200:
-420msg=getString("barbecue","base")%nextDate
-421else:
-422msg=getString("barbecue","eternity")%nextDate
-423
-424msg=url+". "+msg
-425returnmsg
-426
-427defnextBBQ():
-428"""
-429 Calculate the next grillcon date after today
-430 """
-431
-432MAY=5
-433SEPTEMBER=9
-434
-435after=datetime.date.today()
-436spring=thirdFridayIn(after.year,MAY)
-437ifafter<=spring:
-438returnspring
-439
-440autumn=thirdFridayIn(after.year,SEPTEMBER)
-441ifafter<=autumn:
-442returnautumn
-443
-444returnthirdFridayIn(after.year+1,MAY)
-445
-446
-447defthirdFridayIn(y,m):
-448"""
-449 Get the third Friday in a given month and year
-450 """
-451THIRD=2
-452FRIDAY=-1
-453
-454# Start the weeks on saturday to prevent fridays from previous month
-455cal=calendar.Calendar(firstweekday=calendar.SATURDAY)
-456
-457# Return the friday in the third week
-458returncal.monthdatescalendar(y,m)[THIRD][FRIDAY]
-459
-460
-461defmarvinBirthday(row):
-462"""
-463 Check birthday info
-464 """
-465msg=None
-466ifany(rinrowforrin["birthday","födelsedag"]):
-467try:
-468url=getString("birthday","url")
-469soup=BeautifulSoup(urlopen(url),"html.parser")
-470my_list=list()
-471
-472foranainsoup.findAll('a'):
-473ifana.parent.name=='strong':
-474my_list.append(ana.getText())
-475
-476my_list.pop()
-477my_strings=', '.join(my_list)
-478ifnotmy_strings:
-479msg=getString("birthday","nobody")
-480else:
-481msg=getString("birthday","somebody").format(my_strings)
-482
-483exceptException:
-484msg=getString("birthday","error")
-485
-486returnmsg
-487
-488defmarvinNameday(row):
-489"""
-490 Check current nameday
-491 """
-492msg=None
-493ifany(rinrowforrin["nameday","namnsdag"]):
-494try:
-495now=datetime.datetime.now()
-496raw_url="http://api.dryg.net/dagar/v2.1/{year}/{month}/{day}"
-497url=raw_url.format(year=now.year,month=now.month,day=now.day)
-498r=requests.get(url,timeout=5)
-499nameday_data=r.json()
-500names=nameday_data["dagar"][0]["namnsdag"]
-501ifnames:
-502msg=getString("nameday","somebody").format(",".join(names))
-503else:
-504msg=getString("nameday","nobody")
-505exceptException:
-506msg=getString("nameday","error")
-507returnmsg
-508
-509defmarvinUptime(row):
-510"""
-511 Display info about uptime tournament
-512 """
-513msg=None
-514if"uptime"inrow:
-515msg=getString("uptime","info")
-516returnmsg
-517
-518defmarvinStream(row):
-519"""
-520 Display info about stream
-521 """
-522msg=None
-523ifany(rinrowforrin["stream","streama","ström","strömma"]):
-524msg=getString("stream","info")
-525returnmsg
-526
-527defmarvinPrinciple(row):
-528"""
-529 Display one selected software principle, or provide one as random
-530 """
-531msg=None
-532ifany(rinrowforrin["principle","princip","principer"]):
-533principles=getString("principle")
-534principleKeys=list(principles.keys())
-535matchedKeys=[kforkinrowifkinprincipleKeys]
-536ifmatchedKeys:
-537msg=principles[matchedKeys.pop()]
-538else:
-539msg=principles[random.choice(principleKeys)]
-540returnmsg
-541
-542defgetJoke():
-543"""
-544 Retrieves joke from api.chucknorris.io/jokes/random?category=dev
-545 """
-546try:
-547url=getString("joke","url")
-548r=requests.get(url,timeout=5)
-549joke_data=r.json()
-550returnjoke_data["value"]
-551exceptException:
-552returngetString("joke","error")
-553
-554defmarvinJoke(row):
-555"""
-556 Display a random Chuck Norris joke
-557 """
-558msg=None
-559ifany(rinrowforrin["joke","skämt","chuck norris","chuck","norris"]):
-560msg=getJoke()
-561returnmsg
-562
-563defgetCommit():
-564"""
-565 Retrieves random commit message from whatthecommit.com/index.html
-566 """
-567try:
-568url=getString("commit","url")
-569r=requests.get(url,timeout=5)
-570res=r.text.strip()
-571returnres
-572exceptException:
-573returngetString("commit","error")
-574
-575defmarvinCommit(row):
-576"""
-577 Display a random commit message
-578 """
-579msg=None
-580ifany(rinrowforrin["commit","-m"]):
-581commitMsg=getCommit()
-582msg="Använd detta meddelandet: '{}'".format(commitMsg)
-583returnmsg
-
98defwordsAfterKeyWords(words,keyWords):
- 99"""
-100 Return all items in the words list after the first occurence
-101 of an item in the keyWords list.
-102 """
-103kwIndex=[]
-104forkwinkeyWords:
-105ifkwinwords:
-106kwIndex.append(words.index(kw))
-107
-108ifnotkwIndex:
-109returnNone
-110
-111returnwords[min(kwIndex)+1:]
-
-
-
-
Return all items in the words list after the first occurence
-of an item in the keyWords list.
-
-
-
-
-
-
-
-
- def
- marvinGoogle(row):
-
-
-
-
-
-
114defmarvinGoogle(row):
-115"""
-116 Let Marvin present an url to google.
-117 """
-118query=wordsAfterKeyWords(row,["google","googla"])
-119ifnotquery:
-120returnNone
-121
-122searchStr=" ".join(query)
-123url="https://www.google.se/search?q="
-124url+=quote_plus(searchStr)
-125msg=getString("google")
-126returnmsg.format(url)
-
-
-
-
Let Marvin present an url to google.
-
-
-
-
-
-
-
-
- def
- marvinExplainShell(row):
-
-
-
-
-
-
129defmarvinExplainShell(row):
-130"""
-131 Let Marvin present an url to the service explain shell to
-132 explain a shell command.
-133 """
-134query=wordsAfterKeyWords(row,["explain","förklara"])
-135ifnotquery:
-136returnNone
-137cmd=" ".join(query)
-138url="http://explainshell.com/explain?cmd="
-139url+=quote_plus(cmd,"/:")
-140msg=getString("explainShell")
-141returnmsg.format(url)
-
-
-
-
Let Marvin present an url to the service explain shell to
-explain a shell command.
-
-
-
-
-
-
-
-
- def
- marvinSource(row):
-
-
-
-
-
-
144defmarvinSource(row):
-145"""
-146 State message about sourcecode.
-147 """
-148msg=None
-149ifany(rinrowforrin["källkod","source"]):
-150msg=getString("source")
-151
-152returnmsg
-
-
-
-
State message about sourcecode.
-
-
-
-
-
-
-
-
- def
- marvinBudord(row):
-
-
-
-
-
-
155defmarvinBudord(row):
-156"""
-157 What are the budord for Marvin?
-158 """
-159msg=None
-160ifany(rinrowforrin["budord","stentavla"]):
-161ifany(rinrowforrin["#1","1"]):
-162msg=getString("budord","#1")
-163elifany(rinrowforrin["#2","2"]):
-164msg=getString("budord","#2")
-165elifany(rinrowforrin["#3","3"]):
-166msg=getString("budord","#3")
-167elifany(rinrowforrin["#4","4"]):
-168msg=getString("budord","#4")
-169elifany(rinrowforrin["#5","5"]):
-170msg=getString("budord","#5")
-171
-172returnmsg
-
-
-
-
What are the budord for Marvin?
-
-
-
-
-
-
-
-
- def
- marvinQuote(row):
-
-
-
-
-
-
175defmarvinQuote(row):
-176"""
-177 Make a quote.
-178 """
-179msg=None
-180ifany(rinrowforrin["quote","citat","filosofi","filosofera"]):
-181msg=getString("hitchhiker")
-182
-183returnmsg
-
-
-
-
Make a quote.
-
-
-
-
-
-
-
-
- def
- videoOfToday():
-
-
-
-
-
-
186defvideoOfToday():
-187"""
-188 Check what day it is and provide a url to a suitable video together with a greeting.
-189 """
-190dayNum=datetime.date.weekday(datetime.date.today())+1
-191msg=getString("weekdays",str(dayNum))
-192video=getString("video-of-today",str(dayNum))
-193
-194ifvideo:
-195msg+=" En passande video är "+video
-196else:
-197msg+=" Jag har ännu ingen passande video för denna dagen."
-198
-199returnmsg
-
-
-
-
Check what day it is and provide a url to a suitable video together with a greeting.
-
-
-
-
-
-
-
-
- def
- marvinVideoOfToday(row):
-
-
-
-
-
-
202defmarvinVideoOfToday(row):
-203"""
-204 Show the video of today.
-205 """
-206msg=None
-207ifany(rinrowforrin["idag","dagens"]):
-208ifany(rinrowforrin["video","youtube","tube"]):
-209msg=videoOfToday()
-210
-211returnmsg
-
-
-
-
Show the video of today.
-
-
-
-
-
-
-
-
- def
- marvinWhoIs(row):
-
-
-
-
-
-
214defmarvinWhoIs(row):
-215"""
-216 Who is Marvin.
-217 """
-218msg=None
-219ifall(rinrowforrin["vem","är"]):
-220msg=getString("whois")
-221
-222returnmsg
-
-
-
-
Who is Marvin.
-
-
-
-
-
-
-
-
- def
- marvinHelp(row):
-
-
-
-
-
-
225defmarvinHelp(row):
-226"""
-227 Provide a menu.
-228 """
-229msg=None
-230ifany(rinrowforrin["hjälp","help","menu","meny"]):
-231msg=getString("menu")
-232
-233returnmsg
-
-
-
-
Provide a menu.
-
-
-
-
-
-
-
-
- def
- marvinStats(row):
-
-
-
-
-
-
236defmarvinStats(row):
-237"""
-238 Provide a link to the stats.
-239 """
-240msg=None
-241ifany(rinrowforrin["stats","statistik","ircstats"]):
-242msg=getString("ircstats")
-243
-244returnmsg
-
-
-
-
Provide a link to the stats.
-
-
-
-
-
-
-
-
- def
- marvinIrcLog(row):
-
-
-
-
-
-
247defmarvinIrcLog(row):
-248"""
-249 Provide a link to the irclog
-250 """
-251msg=None
-252ifany(rinrowforrin["irc","irclog","log","irclogg","logg","historik"]):
-253msg=getString("irclog")
-254
-255returnmsg
-
-
-
-
Provide a link to the irclog
-
-
-
-
-
-
-
-
- def
- marvinSayHi(row):
-
-
-
-
-
-
258defmarvinSayHi(row):
-259"""
-260 Say hi with a nice message.
-261 """
-262msg=None
-263ifany(rinrowforrin[
-264"snälla","hej","tjena","morsning","morrn","mår","hallå",
-265"halloj","läget","snäll","duktig","träna","träning",
-266"utbildning","tack","tacka","tackar","tacksam"
-267]):
-268smile=getString("smile")
-269hello=getString("hello")
-270friendly=getString("friendly")
-271msg="{}{}{}".format(smile,hello,friendly)
-272
-273returnmsg
-
-
-
-
Say hi with a nice message.
-
-
-
-
-
-
-
-
- def
- marvinLunch(row):
-
-
-
-
-
-
276defmarvinLunch(row):
-277"""
-278 Help decide where to eat.
-279 """
-280lunchOptions={
-281'stan centrum karlskrona kna':'lunch-karlskrona',
-282'ängelholm angelholm engelholm':'lunch-angelholm',
-283'hässleholm hassleholm':'lunch-hassleholm',
-284'malmö malmo malmoe':'lunch-malmo',
-285'göteborg goteborg gbg':'lunch-goteborg'
-286}
-287
-288ifany(rinrowforrin["lunch","mat","äta","luncha"]):
-289lunchStr=getString('lunch-message')
-290
-291forkeys,valueinlunchOptions.items():
-292ifany(rinrowforrinkeys.split(" ")):
-293returnlunchStr.format(getString(value))
-294
-295returnlunchStr.format(getString('lunch-bth'))
-296
-297returnNone
-
376defmarvinStrip(row):
-377"""
-378 Get a comic strip.
-379 """
-380msg=None
-381ifany(rinrowforrin["strip","comic","nöje","paus"]):
-382msg=commitStrip(randomize=any(rinrowforrin["rand","random","slump","lucky"]))
-383returnmsg
-
-
-
-
Get a comic strip.
-
-
-
-
-
-
-
-
- def
- commitStrip(randomize=False):
-
-
-
-
-
-
386defcommitStrip(randomize=False):
-387"""
-388 Latest or random comic strip from CommitStrip.
-389 """
-390msg=getString("commitstrip","message")
-391
-392ifrandomize:
-393first=getString("commitstrip","first")
-394last=getString("commitstrip","last")
-395rand=random.randint(first,last)
-396url=getString("commitstrip","urlPage")+str(rand)
-397else:
-398url=getString("commitstrip","url")
-399
-400returnmsg.format(url=url)
-
-
-
-
Latest or random comic strip from CommitStrip.
-
-
-
-
-
-
-
-
- def
- marvinTimeToBBQ(row):
-
-
-
-
-
-
403defmarvinTimeToBBQ(row):
-404"""
-405 Calcuate the time to next barbecue and print a appropriate msg
-406 """
-407msg=None
-408ifany(rinrowforrin["grilla","grill","grillcon","bbq"]):
-409url=getString("barbecue","url")
-410nextDate=nextBBQ()
-411today=datetime.date.today()
-412daysRemaining=(nextDate-today).days
-413
-414ifdaysRemaining==0:
-415msg=getString("barbecue","today")
-416elifdaysRemaining==1:
-417msg=getString("barbecue","tomorrow")
-418elif1<daysRemaining<14:
-419msg=getString("barbecue","week")%nextDate
-420elif14<daysRemaining<200:
-421msg=getString("barbecue","base")%nextDate
-422else:
-423msg=getString("barbecue","eternity")%nextDate
-424
-425msg=url+". "+msg
-426returnmsg
-
-
-
-
Calcuate the time to next barbecue and print a appropriate msg
-
-
-
-
-
-
-
-
- def
- nextBBQ():
-
-
-
-
-
-
428defnextBBQ():
-429"""
-430 Calculate the next grillcon date after today
-431 """
-432
-433MAY=5
-434SEPTEMBER=9
-435
-436after=datetime.date.today()
-437spring=thirdFridayIn(after.year,MAY)
-438ifafter<=spring:
-439returnspring
-440
-441autumn=thirdFridayIn(after.year,SEPTEMBER)
-442ifafter<=autumn:
-443returnautumn
-444
-445returnthirdFridayIn(after.year+1,MAY)
-
-
-
-
Calculate the next grillcon date after today
-
-
-
-
-
-
-
-
- def
- thirdFridayIn(y, m):
-
-
-
-
-
-
448defthirdFridayIn(y,m):
-449"""
-450 Get the third Friday in a given month and year
-451 """
-452THIRD=2
-453FRIDAY=-1
-454
-455# Start the weeks on saturday to prevent fridays from previous month
-456cal=calendar.Calendar(firstweekday=calendar.SATURDAY)
-457
-458# Return the friday in the third week
-459returncal.monthdatescalendar(y,m)[THIRD][FRIDAY]
-
510defmarvinUptime(row):
-511"""
-512 Display info about uptime tournament
-513 """
-514msg=None
-515if"uptime"inrow:
-516msg=getString("uptime","info")
-517returnmsg
-
-
-
-
Display info about uptime tournament
-
-
-
-
-
-
-
-
- def
- marvinStream(row):
-
-
-
-
-
-
519defmarvinStream(row):
-520"""
-521 Display info about stream
-522 """
-523msg=None
-524ifany(rinrowforrin["stream","streama","ström","strömma"]):
-525msg=getString("stream","info")
-526returnmsg
-
-
-
-
Display info about stream
-
-
-
-
-
-
-
-
- def
- marvinPrinciple(row):
-
-
-
-
-
-
528defmarvinPrinciple(row):
-529"""
-530 Display one selected software principle, or provide one as random
-531 """
-532msg=None
-533ifany(rinrowforrin["principle","princip","principer"]):
-534principles=getString("principle")
-535principleKeys=list(principles.keys())
-536matchedKeys=[kforkinrowifkinprincipleKeys]
-537ifmatchedKeys:
-538msg=principles[matchedKeys.pop()]
-539else:
-540msg=principles[random.choice(principleKeys)]
-541returnmsg
-
-
-
-
Display one selected software principle, or provide one as random
Marvin says Good morning after someone else says it
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/docs/pdoc/search.js b/docs/pdoc/search.js
deleted file mode 100644
index 013a072..0000000
--- a/docs/pdoc/search.js
+++ /dev/null
@@ -1,46 +0,0 @@
-window.pdocSearch = (function(){
-/** elasticlunr - http://weixsong.github.io * Copyright (C) 2017 Oliver Nightingale * Copyright (C) 2017 Wei Song * MIT Licensed */!function(){function e(e){if(null===e||"object"!=typeof e)return e;var t=e.constructor();for(var n in e)e.hasOwnProperty(n)&&(t[n]=e[n]);return t}var t=function(e){var n=new t.Index;return n.pipeline.add(t.trimmer,t.stopWordFilter,t.stemmer),e&&e.call(n,n),n};t.version="0.9.5",lunr=t,t.utils={},t.utils.warn=function(e){return function(t){e.console&&console.warn&&console.warn(t)}}(this),t.utils.toString=function(e){return void 0===e||null===e?"":e.toString()},t.EventEmitter=function(){this.events={}},t.EventEmitter.prototype.addListener=function(){var e=Array.prototype.slice.call(arguments),t=e.pop(),n=e;if("function"!=typeof t)throw new TypeError("last argument must be a function");n.forEach(function(e){this.hasHandler(e)||(this.events[e]=[]),this.events[e].push(t)},this)},t.EventEmitter.prototype.removeListener=function(e,t){if(this.hasHandler(e)){var n=this.events[e].indexOf(t);-1!==n&&(this.events[e].splice(n,1),0==this.events[e].length&&delete this.events[e])}},t.EventEmitter.prototype.emit=function(e){if(this.hasHandler(e)){var t=Array.prototype.slice.call(arguments,1);this.events[e].forEach(function(e){e.apply(void 0,t)},this)}},t.EventEmitter.prototype.hasHandler=function(e){return e in this.events},t.tokenizer=function(e){if(!arguments.length||null===e||void 0===e)return[];if(Array.isArray(e)){var n=e.filter(function(e){return null===e||void 0===e?!1:!0});n=n.map(function(e){return t.utils.toString(e).toLowerCase()});var i=[];return n.forEach(function(e){var n=e.split(t.tokenizer.seperator);i=i.concat(n)},this),i}return e.toString().trim().toLowerCase().split(t.tokenizer.seperator)},t.tokenizer.defaultSeperator=/[\s\-]+/,t.tokenizer.seperator=t.tokenizer.defaultSeperator,t.tokenizer.setSeperator=function(e){null!==e&&void 0!==e&&"object"==typeof e&&(t.tokenizer.seperator=e)},t.tokenizer.resetSeperator=function(){t.tokenizer.seperator=t.tokenizer.defaultSeperator},t.tokenizer.getSeperator=function(){return t.tokenizer.seperator},t.Pipeline=function(){this._queue=[]},t.Pipeline.registeredFunctions={},t.Pipeline.registerFunction=function(e,n){n in t.Pipeline.registeredFunctions&&t.utils.warn("Overwriting existing registered function: "+n),e.label=n,t.Pipeline.registeredFunctions[n]=e},t.Pipeline.getRegisteredFunction=function(e){return e in t.Pipeline.registeredFunctions!=!0?null:t.Pipeline.registeredFunctions[e]},t.Pipeline.warnIfFunctionNotRegistered=function(e){var n=e.label&&e.label in this.registeredFunctions;n||t.utils.warn("Function is not registered with pipeline. This may cause problems when serialising the index.\n",e)},t.Pipeline.load=function(e){var n=new t.Pipeline;return e.forEach(function(e){var i=t.Pipeline.getRegisteredFunction(e);if(!i)throw new Error("Cannot load un-registered function: "+e);n.add(i)}),n},t.Pipeline.prototype.add=function(){var e=Array.prototype.slice.call(arguments);e.forEach(function(e){t.Pipeline.warnIfFunctionNotRegistered(e),this._queue.push(e)},this)},t.Pipeline.prototype.after=function(e,n){t.Pipeline.warnIfFunctionNotRegistered(n);var i=this._queue.indexOf(e);if(-1===i)throw new Error("Cannot find existingFn");this._queue.splice(i+1,0,n)},t.Pipeline.prototype.before=function(e,n){t.Pipeline.warnIfFunctionNotRegistered(n);var i=this._queue.indexOf(e);if(-1===i)throw new Error("Cannot find existingFn");this._queue.splice(i,0,n)},t.Pipeline.prototype.remove=function(e){var t=this._queue.indexOf(e);-1!==t&&this._queue.splice(t,1)},t.Pipeline.prototype.run=function(e){for(var t=[],n=e.length,i=this._queue.length,o=0;n>o;o++){for(var r=e[o],s=0;i>s&&(r=this._queue[s](r,o,e),void 0!==r&&null!==r);s++);void 0!==r&&null!==r&&t.push(r)}return t},t.Pipeline.prototype.reset=function(){this._queue=[]},t.Pipeline.prototype.get=function(){return this._queue},t.Pipeline.prototype.toJSON=function(){return this._queue.map(function(e){return t.Pipeline.warnIfFunctionNotRegistered(e),e.label})},t.Index=function(){this._fields=[],this._ref="id",this.pipeline=new t.Pipeline,this.documentStore=new t.DocumentStore,this.index={},this.eventEmitter=new t.EventEmitter,this._idfCache={},this.on("add","remove","update",function(){this._idfCache={}}.bind(this))},t.Index.prototype.on=function(){var e=Array.prototype.slice.call(arguments);return this.eventEmitter.addListener.apply(this.eventEmitter,e)},t.Index.prototype.off=function(e,t){return this.eventEmitter.removeListener(e,t)},t.Index.load=function(e){e.version!==t.version&&t.utils.warn("version mismatch: current "+t.version+" importing "+e.version);var n=new this;n._fields=e.fields,n._ref=e.ref,n.documentStore=t.DocumentStore.load(e.documentStore),n.pipeline=t.Pipeline.load(e.pipeline),n.index={};for(var i in e.index)n.index[i]=t.InvertedIndex.load(e.index[i]);return n},t.Index.prototype.addField=function(e){return this._fields.push(e),this.index[e]=new t.InvertedIndex,this},t.Index.prototype.setRef=function(e){return this._ref=e,this},t.Index.prototype.saveDocument=function(e){return this.documentStore=new t.DocumentStore(e),this},t.Index.prototype.addDoc=function(e,n){if(e){var n=void 0===n?!0:n,i=e[this._ref];this.documentStore.addDoc(i,e),this._fields.forEach(function(n){var o=this.pipeline.run(t.tokenizer(e[n]));this.documentStore.addFieldLength(i,n,o.length);var r={};o.forEach(function(e){e in r?r[e]+=1:r[e]=1},this);for(var s in r){var u=r[s];u=Math.sqrt(u),this.index[n].addToken(s,{ref:i,tf:u})}},this),n&&this.eventEmitter.emit("add",e,this)}},t.Index.prototype.removeDocByRef=function(e){if(e&&this.documentStore.isDocStored()!==!1&&this.documentStore.hasDoc(e)){var t=this.documentStore.getDoc(e);this.removeDoc(t,!1)}},t.Index.prototype.removeDoc=function(e,n){if(e){var n=void 0===n?!0:n,i=e[this._ref];this.documentStore.hasDoc(i)&&(this.documentStore.removeDoc(i),this._fields.forEach(function(n){var o=this.pipeline.run(t.tokenizer(e[n]));o.forEach(function(e){this.index[n].removeToken(e,i)},this)},this),n&&this.eventEmitter.emit("remove",e,this))}},t.Index.prototype.updateDoc=function(e,t){var t=void 0===t?!0:t;this.removeDocByRef(e[this._ref],!1),this.addDoc(e,!1),t&&this.eventEmitter.emit("update",e,this)},t.Index.prototype.idf=function(e,t){var n="@"+t+"/"+e;if(Object.prototype.hasOwnProperty.call(this._idfCache,n))return this._idfCache[n];var i=this.index[t].getDocFreq(e),o=1+Math.log(this.documentStore.length/(i+1));return this._idfCache[n]=o,o},t.Index.prototype.getFields=function(){return this._fields.slice()},t.Index.prototype.search=function(e,n){if(!e)return[];e="string"==typeof e?{any:e}:JSON.parse(JSON.stringify(e));var i=null;null!=n&&(i=JSON.stringify(n));for(var o=new t.Configuration(i,this.getFields()).get(),r={},s=Object.keys(e),u=0;u0&&t.push(e);for(var i in n)"docs"!==i&&"df"!==i&&this.expandToken(e+i,t,n[i]);return t},t.InvertedIndex.prototype.toJSON=function(){return{root:this.root}},t.Configuration=function(e,n){var e=e||"";if(void 0==n||null==n)throw new Error("fields should not be null");this.config={};var i;try{i=JSON.parse(e),this.buildUserConfig(i,n)}catch(o){t.utils.warn("user configuration parse failed, will use default configuration"),this.buildDefaultConfig(n)}},t.Configuration.prototype.buildDefaultConfig=function(e){this.reset(),e.forEach(function(e){this.config[e]={boost:1,bool:"OR",expand:!1}},this)},t.Configuration.prototype.buildUserConfig=function(e,n){var i="OR",o=!1;if(this.reset(),"bool"in e&&(i=e.bool||i),"expand"in e&&(o=e.expand||o),"fields"in e)for(var r in e.fields)if(n.indexOf(r)>-1){var s=e.fields[r],u=o;void 0!=s.expand&&(u=s.expand),this.config[r]={boost:s.boost||0===s.boost?s.boost:1,bool:s.bool||i,expand:u}}else t.utils.warn("field name in user configuration not found in index instance fields");else this.addAllFields2UserConfig(i,o,n)},t.Configuration.prototype.addAllFields2UserConfig=function(e,t,n){n.forEach(function(n){this.config[n]={boost:1,bool:e,expand:t}},this)},t.Configuration.prototype.get=function(){return this.config},t.Configuration.prototype.reset=function(){this.config={}},lunr.SortedSet=function(){this.length=0,this.elements=[]},lunr.SortedSet.load=function(e){var t=new this;return t.elements=e,t.length=e.length,t},lunr.SortedSet.prototype.add=function(){var e,t;for(e=0;e1;){if(r===e)return o;e>r&&(t=o),r>e&&(n=o),i=n-t,o=t+Math.floor(i/2),r=this.elements[o]}return r===e?o:-1},lunr.SortedSet.prototype.locationFor=function(e){for(var t=0,n=this.elements.length,i=n-t,o=t+Math.floor(i/2),r=this.elements[o];i>1;)e>r&&(t=o),r>e&&(n=o),i=n-t,o=t+Math.floor(i/2),r=this.elements[o];return r>e?o:e>r?o+1:void 0},lunr.SortedSet.prototype.intersect=function(e){for(var t=new lunr.SortedSet,n=0,i=0,o=this.length,r=e.length,s=this.elements,u=e.elements;;){if(n>o-1||i>r-1)break;s[n]!==u[i]?s[n]u[i]&&i++:(t.add(s[n]),n++,i++)}return t},lunr.SortedSet.prototype.clone=function(){var e=new lunr.SortedSet;return e.elements=this.toArray(),e.length=e.elements.length,e},lunr.SortedSet.prototype.union=function(e){var t,n,i;this.length>=e.length?(t=this,n=e):(t=e,n=this),i=t.clone();for(var o=0,r=n.toArray();oModule for the common base class for all Bots\n"}, "bot.Bot": {"fullname": "bot.Bot", "modulename": "bot", "qualname": "Bot", "kind": "class", "doc": "
Base class for things common between different protocols
An IRC bot that answers random questions, keeps a log from the IRC-chat,\neasy to integrate in a webpage and montores a phpBB forum for latest topics\nby loggin in to the forum and checking the RSS-feed.
Check out the file 'marvin_config_default.json' on how to configure, instead\nof using cli-options. The default configfile is 'marvin_config.json' but you\ncan change that using cli-options.
\n\n
Make own actions
\n\n
Check the file 'marvin_strings.json' for the file where most of the strings\nare defined and check out 'marvin_actions.py' to see how to write your own\nactions. Its just a small function.
\n\n
Read from incoming
\n\n
Marvin reads messages from the incoming/ directory, if it exists, and writes\nit out the the irc channel.
1#! /usr/bin/env python3
- 2# -*- coding: utf-8 -*-
- 3
- 4"""
- 5Tests for the main launcher
- 6"""
- 7
- 8importargparse
- 9importcontextlib
- 10importio
- 11importos
- 12importsys
- 13fromunittestimportTestCase
- 14
- 15frommainimportmergeOptionsWithConfigFile,parseOptions,determineProtocol,MSG_VERSION,createBot
- 16fromirc_botimportIrcBot
- 17fromdiscord_botimportDiscordBot
- 18
- 19
- 20classConfigMergeTest(TestCase):
- 21"""Test merging a config file with a dict"""
- 22
- 23defassertMergedConfig(self,config,fileName,expected):
- 24"""Merge dict with file and assert the result matches expected"""
- 25configFile=os.path.join("testConfigs",f"{fileName}.json")
- 26actualConfig=mergeOptionsWithConfigFile(config,configFile)
- 27self.assertEqual(actualConfig,expected)
- 28
- 29
- 30deftestEmpty(self):
- 31"""Empty into empty should equal empty"""
- 32self.assertMergedConfig({},"empty",{})
- 33
- 34deftestAddSingleParameter(self):
- 35"""Add a single parameter to an empty config"""
- 36new={
- 37"single":"test"
- 38}
- 39expected={
- 40"single":"test"
- 41}
- 42self.assertMergedConfig(new,"empty",expected)
- 43
- 44deftestAddSingleParameterOverwrites(self):
- 45"""Add a single parameter to a config that contains it already"""
- 46new={
- 47"single":"test"
- 48}
- 49expected={
- 50"single":"original"
- 51}
- 52self.assertMergedConfig(new,"single",expected)
- 53
- 54deftestAddSingleParameterMerges(self):
- 55"""Add a single parameter to a config that contains a different one"""
- 56new={
- 57"new":"test"
- 58}
- 59expected={
- 60"new":"test",
- 61"single":"original"
- 62}
- 63self.assertMergedConfig(new,"single",expected)
- 64
- 65classConfigParseTest(TestCase):
- 66"""Test parsing options into a config"""
- 67
- 68SAMPLE_CONFIG={
- 69"server":"localhost",
- 70"port":6667,
- 71"channel":"#dbwebb",
- 72"nick":"marvin",
- 73"realname":"Marvin The All Mighty dbwebb-bot",
- 74"ident":"password"
- 75}
- 76
- 77CHANGED_CONFIG={
- 78"server":"remotehost",
- 79"port":1234,
- 80"channel":"#db-o-webb",
- 81"nick":"imposter",
- 82"realname":"where is marvin?",
- 83"ident":"identify"
- 84}
- 85
- 86deftestOverrideHardcodedParameters(self):
- 87"""Test that all the hard coded parameters can be overridden from commandline"""
- 88forparameterin["server","port","channel","nick","realname","ident"]:
- 89sys.argv=["./main.py",f"--{parameter}",str(self.CHANGED_CONFIG.get(parameter))]
- 90actual=parseOptions(self.SAMPLE_CONFIG)
- 91self.assertEqual(actual.get(parameter),self.CHANGED_CONFIG.get(parameter))
- 92
- 93deftestOverrideMultipleParameters(self):
- 94"""Test that multiple parameters can be overridden from commandline"""
- 95sys.argv=["./main.py","--server","dbwebb.se","--port","5432"]
- 96actual=parseOptions(self.SAMPLE_CONFIG)
- 97self.assertEqual(actual.get("server"),"dbwebb.se")
- 98self.assertEqual(actual.get("port"),5432)
- 99
-100deftestOverrideWithFile(self):
-101"""Test that parameters can be overridden with the --config option"""
-102configFile=os.path.join("testConfigs","server.json")
-103sys.argv=["./main.py","--config",configFile]
-104actual=parseOptions(self.SAMPLE_CONFIG)
-105self.assertEqual(actual.get("server"),"irc.dbwebb.se")
-106
-107deftestOverridePrecedenceConfigFirst(self):
-108"""Test that proper precedence is considered. From most to least significant it should be:
-109 explicit parameter -> parameter in --config file -> default """
-110
-111configFile=os.path.join("testConfigs","server.json")
-112sys.argv=["./main.py","--config",configFile,"--server","important.com"]
-113actual=parseOptions(self.SAMPLE_CONFIG)
-114self.assertEqual(actual.get("server"),"important.com")
-115
-116deftestOverridePrecedenceParameterFirst(self):
-117"""Test that proper precedence is considered. From most to least significant it should be:
-118 explicit parameter -> parameter in --config file -> default """
-119
-120configFile=os.path.join("testConfigs","server.json")
-121sys.argv=["./main.py","--server","important.com","--config",configFile]
-122actual=parseOptions(self.SAMPLE_CONFIG)
-123self.assertEqual(actual.get("server"),"important.com")
-124
-125deftestBannedParameters(self):
-126"""Don't allow config, help and version as parameters, as those options are special"""
-127forbannedParameterin["config","help","version"]:
-128withself.assertRaises(argparse.ArgumentError):
-129parseOptions({bannedParameter:"test"})
-130
-131
-132classFormattingTest(TestCase):
-133"""Test the parameters that cause printouts"""
-134
-135USAGE=("usage: main.py [-h] [-v] [--config CONFIG] [--server SERVER] [--port PORT] "
-136"[--channel CHANNEL] [--nick NICK] [--realname REALNAME] [--ident IDENT]\n"
-137" [{irc,discord}]\n")
-138
-139OPTIONS=("positional arguments:\n {irc,discord}\n\n"
-140"options:\n"
-141" -h, --help show this help message and exit\n"
-142" -v, --version\n"
-143" --config CONFIG\n"
-144" --server SERVER\n"
-145" --port PORT\n"
-146" --channel CHANNEL\n"
-147" --nick NICK\n"
-148" --realname REALNAME\n"
-149" --ident IDENT")
-150
-151
-152@classmethod
-153defsetUpClass(cls):
-154"""Set the terminal width to 160 to prevent the tests from failing on small terminals"""
-155os.environ["COLUMNS"]="160"
-156
-157
-158defassertPrintOption(self,options,returnCode,output):
-159"""Assert that parseOptions returns a certain code and prints a certain output"""
-160withself.assertRaises(SystemExit)ase:
-161s=io.StringIO()
-162withcontextlib.redirect_stdout(s):
-163sys.argv=["./main.py"]+[options]
-164parseOptions(ConfigParseTest.SAMPLE_CONFIG)
-165self.assertEqual(e.exception.code,returnCode)
-166self.assertEqual(s.getvalue(),output+"\n")# extra newline added by print()
-167
-168
-169deftestHelpPrintout(self):
-170"""Test that a help is printed when providing the --help flag"""
-171self.assertPrintOption("--help",0,f"{self.USAGE}\n{self.OPTIONS}")
-172
-173deftestHelpPrintoutShort(self):
-174"""Test that a help is printed when providing the -h flag"""
-175self.assertPrintOption("-h",0,f"{self.USAGE}\n{self.OPTIONS}")
-176
-177deftestVersionPrintout(self):
-178"""Test that the version is printed when provided the --version flag"""
-179self.assertPrintOption("--version",0,MSG_VERSION)
-180
-181deftestVersionPrintoutShort(self):
-182"""Test that the version is printed when provided the -v flag"""
-183self.assertPrintOption("-v",0,MSG_VERSION)
-184
-185deftestUnhandledOption(self):
-186"""Test that unknown options gives an error"""
-187withself.assertRaises(SystemExit)ase:
-188s=io.StringIO()
-189expectedError=f"{self.USAGE}main.py: error: unrecognized arguments: -g\n"
-190withcontextlib.redirect_stderr(s):
-191sys.argv=["./main.py","-g"]
-192parseOptions(ConfigParseTest.SAMPLE_CONFIG)
-193self.assertEqual(e.exception.code,2)
-194self.assertEqual(s.getvalue(),expectedError)
-195
-196deftestUnhandledArgument(self):
-197"""Test that any argument gives an error"""
-198withself.assertRaises(SystemExit)ase:
-199s=io.StringIO()
-200expectedError=(f"{self.USAGE}main.py: error: argument protocol: "
-201"invalid choice: 'arg' (choose from 'irc', 'discord')\n")
-202withcontextlib.redirect_stderr(s):
-203sys.argv=["./main.py","arg"]
-204parseOptions(ConfigParseTest.SAMPLE_CONFIG)
-205self.assertEqual(e.exception.code,2)
-206self.assertEqual(s.getvalue(),expectedError)
-207
-208classTestArgumentParsing(TestCase):
-209"""Test parsing argument to determine whether to launch as irc or discord bot """
-210deftestDetermineDiscordProtocol(self):
-211"""Test that the it's possible to give argument to start the bot as a discord bot"""
-212sys.argv=["main.py","discord"]
-213protocol=determineProtocol()
-214self.assertEqual(protocol,"discord")
-215
-216deftestDetermineIRCProtocol(self):
-217"""Test that the it's possible to give argument to start the bot as an irc bot"""
-218sys.argv=["main.py","irc"]
-219protocol=determineProtocol()
-220self.assertEqual(protocol,"irc")
-221
-222deftestDetermineIRCProtocolisDefault(self):
-223"""Test that if no argument is given, irc is the default"""
-224sys.argv=["main.py"]
-225protocol=determineProtocol()
-226self.assertEqual(protocol,"irc")
-227
-228deftestDetermineConfigThrowsOnInvalidProto(self):
-229"""Test that determineProtocol throws error on unsupported protocols"""
-230sys.argv=["main.py","gopher"]
-231withself.assertRaises(SystemExit)ase:
-232determineProtocol()
-233self.assertEqual(e.exception.code,2)
-234
-235classTestBotFactoryMethod(TestCase):
-236"""Test that createBot returns expected instances of Bots"""
-237deftestCreateIRCBot(self):
-238"""Test that an irc bot can be created"""
-239bot=createBot("irc")
-240self.assertIsInstance(bot,IrcBot)
-241
-242deftestCreateDiscordBot(self):
-243"""Test that a discord bot can be created"""
-244bot=createBot("discord")
-245self.assertIsInstance(bot,DiscordBot)
-246
-247deftestCreateUnsupportedProtocolThrows(self):
-248"""Test that trying to create a bot with an unsupported protocol will throw exception"""
-249withself.assertRaises(ValueError)ase:
-250createBot("gopher")
-251self.assertEqual(str(e.exception),"Unsupported protocol: gopher")
-
-
-
-
-
-
-
-
- class
- ConfigMergeTest(unittest.case.TestCase):
-
-
-
-
-
-
21classConfigMergeTest(TestCase):
-22"""Test merging a config file with a dict"""
-23
-24defassertMergedConfig(self,config,fileName,expected):
-25"""Merge dict with file and assert the result matches expected"""
-26configFile=os.path.join("testConfigs",f"{fileName}.json")
-27actualConfig=mergeOptionsWithConfigFile(config,configFile)
-28self.assertEqual(actualConfig,expected)
-29
-30
-31deftestEmpty(self):
-32"""Empty into empty should equal empty"""
-33self.assertMergedConfig({},"empty",{})
-34
-35deftestAddSingleParameter(self):
-36"""Add a single parameter to an empty config"""
-37new={
-38"single":"test"
-39}
-40expected={
-41"single":"test"
-42}
-43self.assertMergedConfig(new,"empty",expected)
-44
-45deftestAddSingleParameterOverwrites(self):
-46"""Add a single parameter to a config that contains it already"""
-47new={
-48"single":"test"
-49}
-50expected={
-51"single":"original"
-52}
-53self.assertMergedConfig(new,"single",expected)
-54
-55deftestAddSingleParameterMerges(self):
-56"""Add a single parameter to a config that contains a different one"""
-57new={
-58"new":"test"
-59}
-60expected={
-61"new":"test",
-62"single":"original"
-63}
-64self.assertMergedConfig(new,"single",expected)
-
24defassertMergedConfig(self,config,fileName,expected):
-25"""Merge dict with file and assert the result matches expected"""
-26configFile=os.path.join("testConfigs",f"{fileName}.json")
-27actualConfig=mergeOptionsWithConfigFile(config,configFile)
-28self.assertEqual(actualConfig,expected)
-
-
-
-
Merge dict with file and assert the result matches expected
-
-
-
-
-
-
-
-
- def
- testEmpty(self):
-
-
-
-
-
-
31deftestEmpty(self):
-32"""Empty into empty should equal empty"""
-33self.assertMergedConfig({},"empty",{})
-
-
-
-
Empty into empty should equal empty
-
-
-
-
-
-
-
-
- def
- testAddSingleParameter(self):
-
-
-
-
-
-
35deftestAddSingleParameter(self):
-36"""Add a single parameter to an empty config"""
-37new={
-38"single":"test"
-39}
-40expected={
-41"single":"test"
-42}
-43self.assertMergedConfig(new,"empty",expected)
-
45deftestAddSingleParameterOverwrites(self):
-46"""Add a single parameter to a config that contains it already"""
-47new={
-48"single":"test"
-49}
-50expected={
-51"single":"original"
-52}
-53self.assertMergedConfig(new,"single",expected)
-
-
-
-
Add a single parameter to a config that contains it already
55deftestAddSingleParameterMerges(self):
-56"""Add a single parameter to a config that contains a different one"""
-57new={
-58"new":"test"
-59}
-60expected={
-61"new":"test",
-62"single":"original"
-63}
-64self.assertMergedConfig(new,"single",expected)
-
-
-
-
Add a single parameter to a config that contains a different one
-
-
-
-
-
-
Inherited Members
-
-
unittest.case.TestCase
-
TestCase
-
failureException
-
longMessage
-
maxDiff
-
addTypeEqualityFunc
-
addCleanup
-
enterContext
-
addClassCleanup
-
enterClassContext
-
setUp
-
tearDown
-
setUpClass
-
tearDownClass
-
countTestCases
-
defaultTestResult
-
shortDescription
-
id
-
subTest
-
run
-
doCleanups
-
doClassCleanups
-
debug
-
skipTest
-
fail
-
assertFalse
-
assertTrue
-
assertRaises
-
assertWarns
-
assertLogs
-
assertNoLogs
-
assertEqual
-
assertNotEqual
-
assertAlmostEqual
-
assertNotAlmostEqual
-
assertSequenceEqual
-
assertListEqual
-
assertTupleEqual
-
assertSetEqual
-
assertIn
-
assertNotIn
-
assertIs
-
assertIsNot
-
assertDictEqual
-
assertCountEqual
-
assertMultiLineEqual
-
assertLess
-
assertLessEqual
-
assertGreater
-
assertGreaterEqual
-
assertIsNone
-
assertIsNotNone
-
assertIsInstance
-
assertNotIsInstance
-
assertRaisesRegex
-
assertWarnsRegex
-
assertRegex
-
assertNotRegex
-
-
-
-
-
-
-
-
-
- class
- ConfigParseTest(unittest.case.TestCase):
-
-
-
-
-
-
66classConfigParseTest(TestCase):
- 67"""Test parsing options into a config"""
- 68
- 69SAMPLE_CONFIG={
- 70"server":"localhost",
- 71"port":6667,
- 72"channel":"#dbwebb",
- 73"nick":"marvin",
- 74"realname":"Marvin The All Mighty dbwebb-bot",
- 75"ident":"password"
- 76}
- 77
- 78CHANGED_CONFIG={
- 79"server":"remotehost",
- 80"port":1234,
- 81"channel":"#db-o-webb",
- 82"nick":"imposter",
- 83"realname":"where is marvin?",
- 84"ident":"identify"
- 85}
- 86
- 87deftestOverrideHardcodedParameters(self):
- 88"""Test that all the hard coded parameters can be overridden from commandline"""
- 89forparameterin["server","port","channel","nick","realname","ident"]:
- 90sys.argv=["./main.py",f"--{parameter}",str(self.CHANGED_CONFIG.get(parameter))]
- 91actual=parseOptions(self.SAMPLE_CONFIG)
- 92self.assertEqual(actual.get(parameter),self.CHANGED_CONFIG.get(parameter))
- 93
- 94deftestOverrideMultipleParameters(self):
- 95"""Test that multiple parameters can be overridden from commandline"""
- 96sys.argv=["./main.py","--server","dbwebb.se","--port","5432"]
- 97actual=parseOptions(self.SAMPLE_CONFIG)
- 98self.assertEqual(actual.get("server"),"dbwebb.se")
- 99self.assertEqual(actual.get("port"),5432)
-100
-101deftestOverrideWithFile(self):
-102"""Test that parameters can be overridden with the --config option"""
-103configFile=os.path.join("testConfigs","server.json")
-104sys.argv=["./main.py","--config",configFile]
-105actual=parseOptions(self.SAMPLE_CONFIG)
-106self.assertEqual(actual.get("server"),"irc.dbwebb.se")
-107
-108deftestOverridePrecedenceConfigFirst(self):
-109"""Test that proper precedence is considered. From most to least significant it should be:
-110 explicit parameter -> parameter in --config file -> default """
-111
-112configFile=os.path.join("testConfigs","server.json")
-113sys.argv=["./main.py","--config",configFile,"--server","important.com"]
-114actual=parseOptions(self.SAMPLE_CONFIG)
-115self.assertEqual(actual.get("server"),"important.com")
-116
-117deftestOverridePrecedenceParameterFirst(self):
-118"""Test that proper precedence is considered. From most to least significant it should be:
-119 explicit parameter -> parameter in --config file -> default """
-120
-121configFile=os.path.join("testConfigs","server.json")
-122sys.argv=["./main.py","--server","important.com","--config",configFile]
-123actual=parseOptions(self.SAMPLE_CONFIG)
-124self.assertEqual(actual.get("server"),"important.com")
-125
-126deftestBannedParameters(self):
-127"""Don't allow config, help and version as parameters, as those options are special"""
-128forbannedParameterin["config","help","version"]:
-129withself.assertRaises(argparse.ArgumentError):
-130parseOptions({bannedParameter:"test"})
-
87deftestOverrideHardcodedParameters(self):
-88"""Test that all the hard coded parameters can be overridden from commandline"""
-89forparameterin["server","port","channel","nick","realname","ident"]:
-90sys.argv=["./main.py",f"--{parameter}",str(self.CHANGED_CONFIG.get(parameter))]
-91actual=parseOptions(self.SAMPLE_CONFIG)
-92self.assertEqual(actual.get(parameter),self.CHANGED_CONFIG.get(parameter))
-
-
-
-
Test that all the hard coded parameters can be overridden from commandline
94deftestOverrideMultipleParameters(self):
-95"""Test that multiple parameters can be overridden from commandline"""
-96sys.argv=["./main.py","--server","dbwebb.se","--port","5432"]
-97actual=parseOptions(self.SAMPLE_CONFIG)
-98self.assertEqual(actual.get("server"),"dbwebb.se")
-99self.assertEqual(actual.get("port"),5432)
-
-
-
-
Test that multiple parameters can be overridden from commandline
-
-
-
-
-
-
-
-
- def
- testOverrideWithFile(self):
-
-
-
-
-
-
101deftestOverrideWithFile(self):
-102"""Test that parameters can be overridden with the --config option"""
-103configFile=os.path.join("testConfigs","server.json")
-104sys.argv=["./main.py","--config",configFile]
-105actual=parseOptions(self.SAMPLE_CONFIG)
-106self.assertEqual(actual.get("server"),"irc.dbwebb.se")
-
-
-
-
Test that parameters can be overridden with the --config option
108deftestOverridePrecedenceConfigFirst(self):
-109"""Test that proper precedence is considered. From most to least significant it should be:
-110 explicit parameter -> parameter in --config file -> default """
-111
-112configFile=os.path.join("testConfigs","server.json")
-113sys.argv=["./main.py","--config",configFile,"--server","important.com"]
-114actual=parseOptions(self.SAMPLE_CONFIG)
-115self.assertEqual(actual.get("server"),"important.com")
-
-
-
-
Test that proper precedence is considered. From most to least significant it should be:
-explicit parameter -> parameter in --config file -> default
117deftestOverridePrecedenceParameterFirst(self):
-118"""Test that proper precedence is considered. From most to least significant it should be:
-119 explicit parameter -> parameter in --config file -> default """
-120
-121configFile=os.path.join("testConfigs","server.json")
-122sys.argv=["./main.py","--server","important.com","--config",configFile]
-123actual=parseOptions(self.SAMPLE_CONFIG)
-124self.assertEqual(actual.get("server"),"important.com")
-
-
-
-
Test that proper precedence is considered. From most to least significant it should be:
-explicit parameter -> parameter in --config file -> default
-
-
-
-
-
-
-
-
- def
- testBannedParameters(self):
-
-
-
-
-
-
126deftestBannedParameters(self):
-127"""Don't allow config, help and version as parameters, as those options are special"""
-128forbannedParameterin["config","help","version"]:
-129withself.assertRaises(argparse.ArgumentError):
-130parseOptions({bannedParameter:"test"})
-
-
-
-
Don't allow config, help and version as parameters, as those options are special
-
-
-
-
-
-
Inherited Members
-
-
unittest.case.TestCase
-
TestCase
-
failureException
-
longMessage
-
maxDiff
-
addTypeEqualityFunc
-
addCleanup
-
enterContext
-
addClassCleanup
-
enterClassContext
-
setUp
-
tearDown
-
setUpClass
-
tearDownClass
-
countTestCases
-
defaultTestResult
-
shortDescription
-
id
-
subTest
-
run
-
doCleanups
-
doClassCleanups
-
debug
-
skipTest
-
fail
-
assertFalse
-
assertTrue
-
assertRaises
-
assertWarns
-
assertLogs
-
assertNoLogs
-
assertEqual
-
assertNotEqual
-
assertAlmostEqual
-
assertNotAlmostEqual
-
assertSequenceEqual
-
assertListEqual
-
assertTupleEqual
-
assertSetEqual
-
assertIn
-
assertNotIn
-
assertIs
-
assertIsNot
-
assertDictEqual
-
assertCountEqual
-
assertMultiLineEqual
-
assertLess
-
assertLessEqual
-
assertGreater
-
assertGreaterEqual
-
assertIsNone
-
assertIsNotNone
-
assertIsInstance
-
assertNotIsInstance
-
assertRaisesRegex
-
assertWarnsRegex
-
assertRegex
-
assertNotRegex
-
-
-
-
-
-
-
-
-
- class
- FormattingTest(unittest.case.TestCase):
-
-
-
-
-
-
133classFormattingTest(TestCase):
-134"""Test the parameters that cause printouts"""
-135
-136USAGE=("usage: main.py [-h] [-v] [--config CONFIG] [--server SERVER] [--port PORT] "
-137"[--channel CHANNEL] [--nick NICK] [--realname REALNAME] [--ident IDENT]\n"
-138" [{irc,discord}]\n")
-139
-140OPTIONS=("positional arguments:\n {irc,discord}\n\n"
-141"options:\n"
-142" -h, --help show this help message and exit\n"
-143" -v, --version\n"
-144" --config CONFIG\n"
-145" --server SERVER\n"
-146" --port PORT\n"
-147" --channel CHANNEL\n"
-148" --nick NICK\n"
-149" --realname REALNAME\n"
-150" --ident IDENT")
-151
-152
-153@classmethod
-154defsetUpClass(cls):
-155"""Set the terminal width to 160 to prevent the tests from failing on small terminals"""
-156os.environ["COLUMNS"]="160"
-157
-158
-159defassertPrintOption(self,options,returnCode,output):
-160"""Assert that parseOptions returns a certain code and prints a certain output"""
-161withself.assertRaises(SystemExit)ase:
-162s=io.StringIO()
-163withcontextlib.redirect_stdout(s):
-164sys.argv=["./main.py"]+[options]
-165parseOptions(ConfigParseTest.SAMPLE_CONFIG)
-166self.assertEqual(e.exception.code,returnCode)
-167self.assertEqual(s.getvalue(),output+"\n")# extra newline added by print()
-168
-169
-170deftestHelpPrintout(self):
-171"""Test that a help is printed when providing the --help flag"""
-172self.assertPrintOption("--help",0,f"{self.USAGE}\n{self.OPTIONS}")
-173
-174deftestHelpPrintoutShort(self):
-175"""Test that a help is printed when providing the -h flag"""
-176self.assertPrintOption("-h",0,f"{self.USAGE}\n{self.OPTIONS}")
-177
-178deftestVersionPrintout(self):
-179"""Test that the version is printed when provided the --version flag"""
-180self.assertPrintOption("--version",0,MSG_VERSION)
-181
-182deftestVersionPrintoutShort(self):
-183"""Test that the version is printed when provided the -v flag"""
-184self.assertPrintOption("-v",0,MSG_VERSION)
-185
-186deftestUnhandledOption(self):
-187"""Test that unknown options gives an error"""
-188withself.assertRaises(SystemExit)ase:
-189s=io.StringIO()
-190expectedError=f"{self.USAGE}main.py: error: unrecognized arguments: -g\n"
-191withcontextlib.redirect_stderr(s):
-192sys.argv=["./main.py","-g"]
-193parseOptions(ConfigParseTest.SAMPLE_CONFIG)
-194self.assertEqual(e.exception.code,2)
-195self.assertEqual(s.getvalue(),expectedError)
-196
-197deftestUnhandledArgument(self):
-198"""Test that any argument gives an error"""
-199withself.assertRaises(SystemExit)ase:
-200s=io.StringIO()
-201expectedError=(f"{self.USAGE}main.py: error: argument protocol: "
-202"invalid choice: 'arg' (choose from 'irc', 'discord')\n")
-203withcontextlib.redirect_stderr(s):
-204sys.argv=["./main.py","arg"]
-205parseOptions(ConfigParseTest.SAMPLE_CONFIG)
-206self.assertEqual(e.exception.code,2)
-207self.assertEqual(s.getvalue(),expectedError)
-
- OPTIONS =
-
- 'positional arguments:\n {irc,discord}\n\noptions:\n -h, --help show this help message and exit\n -v, --version\n --config CONFIG\n --server SERVER\n --port PORT\n --channel CHANNEL\n --nick NICK\n --realname REALNAME\n --ident IDENT'
-
-
-
-
-
-
-
-
-
-
-
-
@classmethod
-
- def
- setUpClass(cls):
-
-
-
-
-
-
153@classmethod
-154defsetUpClass(cls):
-155"""Set the terminal width to 160 to prevent the tests from failing on small terminals"""
-156os.environ["COLUMNS"]="160"
-
-
-
-
Set the terminal width to 160 to prevent the tests from failing on small terminals
159defassertPrintOption(self,options,returnCode,output):
-160"""Assert that parseOptions returns a certain code and prints a certain output"""
-161withself.assertRaises(SystemExit)ase:
-162s=io.StringIO()
-163withcontextlib.redirect_stdout(s):
-164sys.argv=["./main.py"]+[options]
-165parseOptions(ConfigParseTest.SAMPLE_CONFIG)
-166self.assertEqual(e.exception.code,returnCode)
-167self.assertEqual(s.getvalue(),output+"\n")# extra newline added by print()
-
-
-
-
Assert that parseOptions returns a certain code and prints a certain output
-
-
-
-
-
-
-
-
- def
- testHelpPrintout(self):
-
-
-
-
-
-
170deftestHelpPrintout(self):
-171"""Test that a help is printed when providing the --help flag"""
-172self.assertPrintOption("--help",0,f"{self.USAGE}\n{self.OPTIONS}")
-
-
-
-
Test that a help is printed when providing the --help flag
-
-
-
-
-
-
-
-
- def
- testHelpPrintoutShort(self):
-
-
-
-
-
-
174deftestHelpPrintoutShort(self):
-175"""Test that a help is printed when providing the -h flag"""
-176self.assertPrintOption("-h",0,f"{self.USAGE}\n{self.OPTIONS}")
-
-
-
-
Test that a help is printed when providing the -h flag
-
-
-
-
-
-
-
-
- def
- testVersionPrintout(self):
-
-
-
-
-
-
178deftestVersionPrintout(self):
-179"""Test that the version is printed when provided the --version flag"""
-180self.assertPrintOption("--version",0,MSG_VERSION)
-
-
-
-
Test that the version is printed when provided the --version flag
-
-
-
-
-
-
-
-
- def
- testVersionPrintoutShort(self):
-
-
-
-
-
-
182deftestVersionPrintoutShort(self):
-183"""Test that the version is printed when provided the -v flag"""
-184self.assertPrintOption("-v",0,MSG_VERSION)
-
-
-
-
Test that the version is printed when provided the -v flag
197deftestUnhandledArgument(self):
-198"""Test that any argument gives an error"""
-199withself.assertRaises(SystemExit)ase:
-200s=io.StringIO()
-201expectedError=(f"{self.USAGE}main.py: error: argument protocol: "
-202"invalid choice: 'arg' (choose from 'irc', 'discord')\n")
-203withcontextlib.redirect_stderr(s):
-204sys.argv=["./main.py","arg"]
-205parseOptions(ConfigParseTest.SAMPLE_CONFIG)
-206self.assertEqual(e.exception.code,2)
-207self.assertEqual(s.getvalue(),expectedError)
-
-
-
-
Test that any argument gives an error
-
-
-
-
-
-
Inherited Members
-
-
unittest.case.TestCase
-
TestCase
-
failureException
-
longMessage
-
maxDiff
-
addTypeEqualityFunc
-
addCleanup
-
enterContext
-
addClassCleanup
-
enterClassContext
-
setUp
-
tearDown
-
tearDownClass
-
countTestCases
-
defaultTestResult
-
shortDescription
-
id
-
subTest
-
run
-
doCleanups
-
doClassCleanups
-
debug
-
skipTest
-
fail
-
assertFalse
-
assertTrue
-
assertRaises
-
assertWarns
-
assertLogs
-
assertNoLogs
-
assertEqual
-
assertNotEqual
-
assertAlmostEqual
-
assertNotAlmostEqual
-
assertSequenceEqual
-
assertListEqual
-
assertTupleEqual
-
assertSetEqual
-
assertIn
-
assertNotIn
-
assertIs
-
assertIsNot
-
assertDictEqual
-
assertCountEqual
-
assertMultiLineEqual
-
assertLess
-
assertLessEqual
-
assertGreater
-
assertGreaterEqual
-
assertIsNone
-
assertIsNotNone
-
assertIsInstance
-
assertNotIsInstance
-
assertRaisesRegex
-
assertWarnsRegex
-
assertRegex
-
assertNotRegex
-
-
-
-
-
-
-
-
-
- class
- TestArgumentParsing(unittest.case.TestCase):
-
-
-
-
-
-
209classTestArgumentParsing(TestCase):
-210"""Test parsing argument to determine whether to launch as irc or discord bot """
-211deftestDetermineDiscordProtocol(self):
-212"""Test that the it's possible to give argument to start the bot as a discord bot"""
-213sys.argv=["main.py","discord"]
-214protocol=determineProtocol()
-215self.assertEqual(protocol,"discord")
-216
-217deftestDetermineIRCProtocol(self):
-218"""Test that the it's possible to give argument to start the bot as an irc bot"""
-219sys.argv=["main.py","irc"]
-220protocol=determineProtocol()
-221self.assertEqual(protocol,"irc")
-222
-223deftestDetermineIRCProtocolisDefault(self):
-224"""Test that if no argument is given, irc is the default"""
-225sys.argv=["main.py"]
-226protocol=determineProtocol()
-227self.assertEqual(protocol,"irc")
-228
-229deftestDetermineConfigThrowsOnInvalidProto(self):
-230"""Test that determineProtocol throws error on unsupported protocols"""
-231sys.argv=["main.py","gopher"]
-232withself.assertRaises(SystemExit)ase:
-233determineProtocol()
-234self.assertEqual(e.exception.code,2)
-
-
-
-
Test parsing argument to determine whether to launch as irc or discord bot
211deftestDetermineDiscordProtocol(self):
-212"""Test that the it's possible to give argument to start the bot as a discord bot"""
-213sys.argv=["main.py","discord"]
-214protocol=determineProtocol()
-215self.assertEqual(protocol,"discord")
-
-
-
-
Test that the it's possible to give argument to start the bot as a discord bot
-
-
-
-
-
-
-
-
- def
- testDetermineIRCProtocol(self):
-
-
-
-
-
-
217deftestDetermineIRCProtocol(self):
-218"""Test that the it's possible to give argument to start the bot as an irc bot"""
-219sys.argv=["main.py","irc"]
-220protocol=determineProtocol()
-221self.assertEqual(protocol,"irc")
-
-
-
-
Test that the it's possible to give argument to start the bot as an irc bot
223deftestDetermineIRCProtocolisDefault(self):
-224"""Test that if no argument is given, irc is the default"""
-225sys.argv=["main.py"]
-226protocol=determineProtocol()
-227self.assertEqual(protocol,"irc")
-
-
-
-
Test that if no argument is given, irc is the default
229deftestDetermineConfigThrowsOnInvalidProto(self):
-230"""Test that determineProtocol throws error on unsupported protocols"""
-231sys.argv=["main.py","gopher"]
-232withself.assertRaises(SystemExit)ase:
-233determineProtocol()
-234self.assertEqual(e.exception.code,2)
-
-
-
-
Test that determineProtocol throws error on unsupported protocols
-
-
-
-
-
-
Inherited Members
-
-
unittest.case.TestCase
-
TestCase
-
failureException
-
longMessage
-
maxDiff
-
addTypeEqualityFunc
-
addCleanup
-
enterContext
-
addClassCleanup
-
enterClassContext
-
setUp
-
tearDown
-
setUpClass
-
tearDownClass
-
countTestCases
-
defaultTestResult
-
shortDescription
-
id
-
subTest
-
run
-
doCleanups
-
doClassCleanups
-
debug
-
skipTest
-
fail
-
assertFalse
-
assertTrue
-
assertRaises
-
assertWarns
-
assertLogs
-
assertNoLogs
-
assertEqual
-
assertNotEqual
-
assertAlmostEqual
-
assertNotAlmostEqual
-
assertSequenceEqual
-
assertListEqual
-
assertTupleEqual
-
assertSetEqual
-
assertIn
-
assertNotIn
-
assertIs
-
assertIsNot
-
assertDictEqual
-
assertCountEqual
-
assertMultiLineEqual
-
assertLess
-
assertLessEqual
-
assertGreater
-
assertGreaterEqual
-
assertIsNone
-
assertIsNotNone
-
assertIsInstance
-
assertNotIsInstance
-
assertRaisesRegex
-
assertWarnsRegex
-
assertRegex
-
assertNotRegex
-
-
-
-
-
-
-
-
-
- class
- TestBotFactoryMethod(unittest.case.TestCase):
-
-
-
-
-
-
236classTestBotFactoryMethod(TestCase):
-237"""Test that createBot returns expected instances of Bots"""
-238deftestCreateIRCBot(self):
-239"""Test that an irc bot can be created"""
-240bot=createBot("irc")
-241self.assertIsInstance(bot,IrcBot)
-242
-243deftestCreateDiscordBot(self):
-244"""Test that a discord bot can be created"""
-245bot=createBot("discord")
-246self.assertIsInstance(bot,DiscordBot)
-247
-248deftestCreateUnsupportedProtocolThrows(self):
-249"""Test that trying to create a bot with an unsupported protocol will throw exception"""
-250withself.assertRaises(ValueError)ase:
-251createBot("gopher")
-252self.assertEqual(str(e.exception),"Unsupported protocol: gopher")
-
-
-
-
Test that createBot returns expected instances of Bots
-
-
-
-
-
-
-
- def
- testCreateIRCBot(self):
-
-
-
-
-
-
238deftestCreateIRCBot(self):
-239"""Test that an irc bot can be created"""
-240bot=createBot("irc")
-241self.assertIsInstance(bot,IrcBot)
-
-
-
-
Test that an irc bot can be created
-
-
-
-
-
-
-
-
- def
- testCreateDiscordBot(self):
-
-
-
-
-
-
243deftestCreateDiscordBot(self):
-244"""Test that a discord bot can be created"""
-245bot=createBot("discord")
-246self.assertIsInstance(bot,DiscordBot)
-
248deftestCreateUnsupportedProtocolThrows(self):
-249"""Test that trying to create a bot with an unsupported protocol will throw exception"""
-250withself.assertRaises(ValueError)ase:
-251createBot("gopher")
-252self.assertEqual(str(e.exception),"Unsupported protocol: gopher")
-
-
-
-
Test that trying to create a bot with an unsupported protocol will throw exception
-
-
-
-
-
-
Inherited Members
-
-
unittest.case.TestCase
-
TestCase
-
failureException
-
longMessage
-
maxDiff
-
addTypeEqualityFunc
-
addCleanup
-
enterContext
-
addClassCleanup
-
enterClassContext
-
setUp
-
tearDown
-
setUpClass
-
tearDownClass
-
countTestCases
-
defaultTestResult
-
shortDescription
-
id
-
subTest
-
run
-
doCleanups
-
doClassCleanups
-
debug
-
skipTest
-
fail
-
assertFalse
-
assertTrue
-
assertRaises
-
assertWarns
-
assertLogs
-
assertNoLogs
-
assertEqual
-
assertNotEqual
-
assertAlmostEqual
-
assertNotAlmostEqual
-
assertSequenceEqual
-
assertListEqual
-
assertTupleEqual
-
assertSetEqual
-
assertIn
-
assertNotIn
-
assertIs
-
assertIsNot
-
assertDictEqual
-
assertCountEqual
-
assertMultiLineEqual
-
assertLess
-
assertLessEqual
-
assertGreater
-
assertGreaterEqual
-
assertIsNone
-
assertIsNotNone
-
assertIsInstance
-
assertNotIsInstance
-
assertRaisesRegex
-
assertWarnsRegex
-
assertRegex
-
assertNotRegex
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/docs/pdoc/test_marvin_actions.html b/docs/pdoc/test_marvin_actions.html
deleted file mode 100644
index b02ab7b..0000000
--- a/docs/pdoc/test_marvin_actions.html
+++ /dev/null
@@ -1,1976 +0,0 @@
-
-
-
-
-
-
- test_marvin_actions API documentation
-
-
-
-
-
-
-
-
-
-
-
-
-test_marvin_actions
-
-
Tests for all Marvin actions
-
-
-
-
-
-
-
1#! /usr/bin/env python3
- 2# -*- coding: utf-8 -*-
- 3
- 4"""
- 5Tests for all Marvin actions
- 6"""
- 7
- 8importjson
- 9
- 10fromdatetimeimportdate
- 11fromunittestimportmock,TestCase
- 12
- 13importrequests
- 14
- 15frombotimportBot
- 16importmarvin_actions
- 17importmarvin_general_actions
- 18
- 19classActionTest(TestCase):
- 20"""Test Marvin actions"""
- 21strings={}
- 22
- 23@classmethod
- 24defsetUpClass(cls):
- 25withopen("marvin_strings.json",encoding="utf-8")asf:
- 26cls.strings=json.load(f)
- 27
- 28
- 29defexecuteAction(self,action,message):
- 30"""Execute an action for a message and return the response"""
- 31returnaction(Bot.tokenize(message))
- 32
- 33
- 34defassertActionOutput(self,action,message,expectedOutput):
- 35"""Call an action on message and assert expected output"""
- 36actualOutput=self.executeAction(action,message)
- 37
- 38self.assertEqual(actualOutput,expectedOutput)
- 39
- 40
- 41defassertActionSilent(self,action,message):
- 42"""Call an action with provided message and assert no output"""
- 43self.assertActionOutput(action,message,None)
- 44
- 45
- 46defassertStringsOutput(self,action,message,expectedoutputKey,subkey=None):
- 47"""Call an action with provided message and assert the output is equal to DB"""
- 48expectedOutput=self.strings.get(expectedoutputKey)
- 49ifsubkeyisnotNone:
- 50ifisinstance(expectedOutput,list):
- 51expectedOutput=expectedOutput[subkey]
- 52else:
- 53expectedOutput=expectedOutput.get(subkey)
- 54self.assertActionOutput(action,message,expectedOutput)
- 55
- 56
- 57defassertBBQResponse(self,todaysDate,bbqDate,expectedMessageKey):
- 58"""Assert that the proper bbq message is returned, given a date"""
- 59url=self.strings.get("barbecue").get("url")
- 60message=self.strings.get("barbecue").get(expectedMessageKey)
- 61ifisinstance(message,list):
- 62message=message[1]
- 63ifexpectedMessageKeyin["base","week","eternity"]:
- 64message=message%bbqDate
- 65
- 66withmock.patch("marvin_actions.datetime")asd:
- 67d.date.today.return_value=todaysDate
- 68withmock.patch("marvin_actions.random")asr:
- 69r.randint.return_value=1
- 70expected=f"{url}. {message}"
- 71self.assertActionOutput(marvin_actions.marvinTimeToBBQ,"dags att grilla",expected)
- 72
- 73
- 74defassertNameDayOutput(self,exampleFile,expectedOutput):
- 75"""Assert that the proper nameday message is returned, given an inputfile"""
- 76withopen(f"namedayFiles/{exampleFile}.json","r",encoding="UTF-8")asf:
- 77response=requests.models.Response()
- 78response._content=str.encode(json.dumps(json.load(f)))
- 79withmock.patch("marvin_actions.requests")asr:
- 80r.get.return_value=response
- 81self.assertActionOutput(marvin_actions.marvinNameday,"nameday",expectedOutput)
- 82
- 83defassertJokeOutput(self,exampleFile,expectedOutput):
- 84"""Assert that a joke is returned, given an input file"""
- 85withopen(f"jokeFiles/{exampleFile}.json","r",encoding="UTF-8")asf:
- 86response=requests.models.Response()
- 87response._content=str.encode(json.dumps(json.load(f)))
- 88withmock.patch("marvin_actions.requests")asr:
- 89r.get.return_value=response
- 90self.assertActionOutput(marvin_actions.marvinJoke,"joke",expectedOutput)
- 91
- 92deftestSmile(self):
- 93"""Test that marvin can smile"""
- 94withmock.patch("marvin_actions.random")asr:
- 95r.randint.return_value=1
- 96self.assertStringsOutput(marvin_actions.marvinSmile,"le lite?","smile",1)
- 97self.assertActionSilent(marvin_actions.marvinSmile,"sur idag?")
- 98
- 99deftestWhois(self):
-100"""Test that marvin responds to whois"""
-101self.assertStringsOutput(marvin_actions.marvinWhoIs,"vem är marvin?","whois")
-102self.assertActionSilent(marvin_actions.marvinWhoIs,"vemär")
-103
-104deftestGoogle(self):
-105"""Test that marvin can help google stuff"""
-106withmock.patch("marvin_actions.random")asr:
-107r.randint.return_value=1
-108self.assertActionOutput(
-109marvin_actions.marvinGoogle,
-110"kan du googla mos",
-111"LMGTFY https://www.google.se/search?q=mos")
-112self.assertActionOutput(
-113marvin_actions.marvinGoogle,
-114"kan du googla google mos",
-115"LMGTFY https://www.google.se/search?q=google+mos")
-116self.assertActionSilent(marvin_actions.marvinGoogle,"du kan googla")
-117self.assertActionSilent(marvin_actions.marvinGoogle,"gogool")
-118
-119deftestExplainShell(self):
-120"""Test that marvin can explain shell commands"""
-121url="http://explainshell.com/explain?cmd=pwd"
-122self.assertActionOutput(marvin_actions.marvinExplainShell,"explain pwd",url)
-123self.assertActionOutput(marvin_actions.marvinExplainShell,"can you explain pwd",url)
-124self.assertActionOutput(
-125marvin_actions.marvinExplainShell,
-126"förklara pwd|grep -o $user",
-127f"{url}%7Cgrep+-o+%24user")
-128
-129self.assertActionSilent(marvin_actions.marvinExplainShell,"explains")
-130
-131deftestSource(self):
-132"""Test that marvin responds to questions about source code"""
-133self.assertStringsOutput(marvin_actions.marvinSource,"source","source")
-134self.assertStringsOutput(marvin_actions.marvinSource,"källkod","source")
-135self.assertActionSilent(marvin_actions.marvinSource,"opensource")
-136
-137deftestBudord(self):
-138"""Test that marvin knows all the commandments"""
-139forninrange(1,5):
-140self.assertStringsOutput(marvin_actions.marvinBudord,f"budord #{n}","budord",f"#{n}")
-141
-142self.assertStringsOutput(marvin_actions.marvinBudord,"visa stentavla 1","budord","#1")
-143self.assertActionSilent(marvin_actions.marvinBudord,"var är stentavlan?")
-144
-145deftestQuote(self):
-146"""Test that marvin can quote The Hitchhikers Guide to the Galaxy"""
-147withmock.patch("marvin_actions.random")asr:
-148r.randint.return_value=1
-149self.assertStringsOutput(marvin_actions.marvinQuote,"ge os ett citat","hitchhiker",1)
-150self.assertStringsOutput(marvin_actions.marvinQuote,"filosofi","hitchhiker",1)
-151self.assertStringsOutput(marvin_actions.marvinQuote,"filosofera","hitchhiker",1)
-152self.assertActionSilent(marvin_actions.marvinQuote,"noquote")
-153
-154fori,_inenumerate(self.strings.get("hitchhiker")):
-155r.randint.return_value=i
-156self.assertStringsOutput(marvin_actions.marvinQuote,"quote","hitchhiker",i)
-157
-158deftestVideoOfToday(self):
-159"""Test that marvin can link to a different video each day of the week"""
-160withmock.patch("marvin_actions.datetime")asdt:
-161fordinrange(1,8):
-162dt.date.weekday.return_value=d-1
-163day=self.strings.get("weekdays").get(str(d))
-164video=self.strings.get("video-of-today").get(str(d))
-165response=f"{day} En passande video är {video}"
-166self.assertActionOutput(marvin_actions.marvinVideoOfToday,"dagens video",response)
-167self.assertActionSilent(marvin_actions.marvinVideoOfToday,"videoidag")
-168
-169deftestHelp(self):
-170"""Test that marvin can provide a help menu"""
-171self.assertStringsOutput(marvin_actions.marvinHelp,"help","menu")
-172self.assertActionSilent(marvin_actions.marvinHelp,"halp")
-173
-174deftestStats(self):
-175"""Test that marvin can provide a link to the IRC stats page"""
-176self.assertStringsOutput(marvin_actions.marvinStats,"stats","ircstats")
-177self.assertActionSilent(marvin_actions.marvinStats,"statistics")
-178
-179deftestIRCLog(self):
-180"""Test that marvin can provide a link to the IRC log"""
-181self.assertStringsOutput(marvin_actions.marvinIrcLog,"irc","irclog")
-182self.assertActionSilent(marvin_actions.marvinIrcLog,"ircstats")
-183
-184deftestSayHi(self):
-185"""Test that marvin responds to greetings"""
-186withmock.patch("marvin_actions.random")asr:
-187forskey,sinenumerate(self.strings.get("smile")):
-188forhkey,hinenumerate(self.strings.get("hello")):
-189forfkey,finenumerate(self.strings.get("friendly")):
-190r.randint.side_effect=[skey,hkey,fkey]
-191self.assertActionOutput(marvin_actions.marvinSayHi,"hej",f"{s}{h}{f}")
-192self.assertActionSilent(marvin_actions.marvinSayHi,"korsning")
-193
-194deftestLunchLocations(self):
-195"""Test that marvin can provide lunch suggestions for certain places"""
-196locations=["karlskrona","goteborg","angelholm","hassleholm","malmo"]
-197withmock.patch("marvin_actions.random")asr:
-198forlocationinlocations:
-199forindex,placeinenumerate(self.strings.get(f"lunch-{location}")):
-200r.randint.side_effect=[0,index]
-201self.assertActionOutput(
-202marvin_actions.marvinLunch,f"mat {location}",f"Ska vi ta {place}?")
-203r.randint.side_effect=[1,2]
-204self.assertActionOutput(
-205marvin_actions.marvinLunch,"dags att luncha","Jag är lite sugen på Indiska?")
-206self.assertActionSilent(marvin_actions.marvinLunch,"matdags")
-207
-208deftestStrip(self):
-209"""Test that marvin can recommend comics"""
-210messageFormat=self.strings.get("commitstrip").get("message")
-211expected=messageFormat.format(url=self.strings.get("commitstrip").get("url"))
-212self.assertActionOutput(marvin_actions.marvinStrip,"lite strip kanske?",expected)
-213self.assertActionSilent(marvin_actions.marvinStrip,"nostrip")
-214
-215deftestRandomStrip(self):
-216"""Test that marvin can recommend random comics"""
-217messageFormat=self.strings.get("commitstrip").get("message")
-218expected=messageFormat.format(url=self.strings.get("commitstrip").get("urlPage")+"123")
-219withmock.patch("marvin_actions.random")asr:
-220r.randint.return_value=123
-221self.assertActionOutput(marvin_actions.marvinStrip,"random strip kanske?",expected)
-222
-223deftestTimeToBBQ(self):
-224"""Test that marvin knows when the next BBQ is"""
-225self.assertBBQResponse(date(2024,5,17),date(2024,5,17),"today")
-226self.assertBBQResponse(date(2024,5,16),date(2024,5,17),"tomorrow")
-227self.assertBBQResponse(date(2024,5,10),date(2024,5,17),"week")
-228self.assertBBQResponse(date(2024,5,1),date(2024,5,17),"base")
-229self.assertBBQResponse(date(2023,10,17),date(2024,5,17),"eternity")
-230
-231self.assertBBQResponse(date(2024,9,20),date(2024,9,20),"today")
-232self.assertBBQResponse(date(2024,9,19),date(2024,9,20),"tomorrow")
-233self.assertBBQResponse(date(2024,9,13),date(2024,9,20),"week")
-234self.assertBBQResponse(date(2024,9,4),date(2024,9,20),"base")
-235
-236deftestNameDayReaction(self):
-237"""Test that marvin only responds to nameday when asked"""
-238self.assertActionSilent(marvin_actions.marvinNameday,"anything")
-239
-240deftestNameDayRequest(self):
-241"""Test that marvin sends a proper request for nameday info"""
-242withmock.patch("marvin_actions.requests")asr:
-243withmock.patch("marvin_actions.datetime")asd:
-244d.datetime.now.return_value=date(2024,1,2)
-245self.executeAction(marvin_actions.marvinNameday,"namnsdag")
-246self.assertEqual(r.get.call_args.args[0],"http://api.dryg.net/dagar/v2.1/2024/1/2")
-247
-248deftestNameDayResponse(self):
-249"""Test that marvin properly parses nameday responses"""
-250self.assertNameDayOutput("single","Idag har Svea namnsdag")
-251self.assertNameDayOutput("double","Idag har Alfred,Alfrida namnsdag")
-252self.assertNameDayOutput("nobody","Ingen har namnsdag idag")
-253
-254deftestJokeRequest(self):
-255"""Test that marvin sends a proper request for a joke"""
-256withmock.patch("marvin_actions.requests")asr:
-257self.executeAction(marvin_actions.marvinJoke,"joke")
-258self.assertEqual(r.get.call_args.args[0],"https://api.chucknorris.io/jokes/random?category=dev")
-259
-260deftestJoke(self):
-261"""Test that marvin sends a joke when requested"""
-262self.assertJokeOutput("joke","There is no Esc key on Chuck Norris' keyboard, because no one escapes Chuck Norris.")
-263
-264deftestUptime(self):
-265"""Test that marvin can provide the link to the uptime tournament"""
-266self.assertStringsOutput(marvin_actions.marvinUptime,"visa lite uptime","uptime","info")
-267self.assertActionSilent(marvin_actions.marvinUptime,"uptimetävling")
-268
-269deftestStream(self):
-270"""Test that marvin can provide the link to the stream"""
-271self.assertStringsOutput(marvin_actions.marvinStream,"ska mos streama?","stream","info")
-272self.assertActionSilent(marvin_actions.marvinStream,"är mos en streamer?")
-273
-274deftestPrinciple(self):
-275"""Test that marvin can recite some software principles"""
-276principles=self.strings.get("principle")
-277forkey,valueinprinciples.items():
-278self.assertActionOutput(marvin_actions.marvinPrinciple,f"princip {key}",value)
-279withmock.patch("marvin_actions.random")asr:
-280r.choice.return_value="dry"
-281self.assertStringsOutput(marvin_actions.marvinPrinciple,"princip","principle","dry")
-282self.assertActionSilent(marvin_actions.marvinPrinciple,"principlös")
-283
-284deftestCommitRequest(self):
-285"""Test that marvin sends proper requests when generating commit messages"""
-286withmock.patch("marvin_actions.requests")asr:
-287self.executeAction(marvin_actions.marvinCommit,"vad skriver man efter commit -m?")
-288self.assertEqual(r.get.call_args.args[0],"http://whatthecommit.com/index.txt")
-289
-290deftestCommitResponse(self):
-291"""Test that marvin properly handles responses when generating commit messages"""
-292message="Secret sauce #9"
-293response=requests.models.Response()
-294response._content=str.encode(message)
-295withmock.patch("marvin_actions.requests")asr:
-296r.get.return_value=response
-297expected=f"Använd detta meddelandet: '{message}'"
-298self.assertActionOutput(marvin_actions.marvinCommit,"commit",expected)
-299
-300deftestMorning(self):
-301"""Test that marvin wishes good morning, at most once per day"""
-302marvin_general_actions.lastDateGreeted=None
-303withmock.patch("marvin_general_actions.datetime")asd:
-304d.date.today.return_value=date(2024,5,17)
-305withmock.patch("marvin_general_actions.random")asr:
-306r.choice.return_value="Morgon"
-307self.assertActionOutput(marvin_general_actions.marvinMorning,"morrn","Morgon")
-308# Should only greet once per day
-309self.assertActionSilent(marvin_general_actions.marvinMorning,"morgon")
-310# Should greet again tomorrow
-311d.date.today.return_value=date(2024,5,18)
-312self.assertActionOutput(marvin_general_actions.marvinMorning,"godmorgon","Morgon")
-
-
-
-
-
-
-
-
- class
- ActionTest(unittest.case.TestCase):
-
-
-
-
-
-
20classActionTest(TestCase):
- 21"""Test Marvin actions"""
- 22strings={}
- 23
- 24@classmethod
- 25defsetUpClass(cls):
- 26withopen("marvin_strings.json",encoding="utf-8")asf:
- 27cls.strings=json.load(f)
- 28
- 29
- 30defexecuteAction(self,action,message):
- 31"""Execute an action for a message and return the response"""
- 32returnaction(Bot.tokenize(message))
- 33
- 34
- 35defassertActionOutput(self,action,message,expectedOutput):
- 36"""Call an action on message and assert expected output"""
- 37actualOutput=self.executeAction(action,message)
- 38
- 39self.assertEqual(actualOutput,expectedOutput)
- 40
- 41
- 42defassertActionSilent(self,action,message):
- 43"""Call an action with provided message and assert no output"""
- 44self.assertActionOutput(action,message,None)
- 45
- 46
- 47defassertStringsOutput(self,action,message,expectedoutputKey,subkey=None):
- 48"""Call an action with provided message and assert the output is equal to DB"""
- 49expectedOutput=self.strings.get(expectedoutputKey)
- 50ifsubkeyisnotNone:
- 51ifisinstance(expectedOutput,list):
- 52expectedOutput=expectedOutput[subkey]
- 53else:
- 54expectedOutput=expectedOutput.get(subkey)
- 55self.assertActionOutput(action,message,expectedOutput)
- 56
- 57
- 58defassertBBQResponse(self,todaysDate,bbqDate,expectedMessageKey):
- 59"""Assert that the proper bbq message is returned, given a date"""
- 60url=self.strings.get("barbecue").get("url")
- 61message=self.strings.get("barbecue").get(expectedMessageKey)
- 62ifisinstance(message,list):
- 63message=message[1]
- 64ifexpectedMessageKeyin["base","week","eternity"]:
- 65message=message%bbqDate
- 66
- 67withmock.patch("marvin_actions.datetime")asd:
- 68d.date.today.return_value=todaysDate
- 69withmock.patch("marvin_actions.random")asr:
- 70r.randint.return_value=1
- 71expected=f"{url}. {message}"
- 72self.assertActionOutput(marvin_actions.marvinTimeToBBQ,"dags att grilla",expected)
- 73
- 74
- 75defassertNameDayOutput(self,exampleFile,expectedOutput):
- 76"""Assert that the proper nameday message is returned, given an inputfile"""
- 77withopen(f"namedayFiles/{exampleFile}.json","r",encoding="UTF-8")asf:
- 78response=requests.models.Response()
- 79response._content=str.encode(json.dumps(json.load(f)))
- 80withmock.patch("marvin_actions.requests")asr:
- 81r.get.return_value=response
- 82self.assertActionOutput(marvin_actions.marvinNameday,"nameday",expectedOutput)
- 83
- 84defassertJokeOutput(self,exampleFile,expectedOutput):
- 85"""Assert that a joke is returned, given an input file"""
- 86withopen(f"jokeFiles/{exampleFile}.json","r",encoding="UTF-8")asf:
- 87response=requests.models.Response()
- 88response._content=str.encode(json.dumps(json.load(f)))
- 89withmock.patch("marvin_actions.requests")asr:
- 90r.get.return_value=response
- 91self.assertActionOutput(marvin_actions.marvinJoke,"joke",expectedOutput)
- 92
- 93deftestSmile(self):
- 94"""Test that marvin can smile"""
- 95withmock.patch("marvin_actions.random")asr:
- 96r.randint.return_value=1
- 97self.assertStringsOutput(marvin_actions.marvinSmile,"le lite?","smile",1)
- 98self.assertActionSilent(marvin_actions.marvinSmile,"sur idag?")
- 99
-100deftestWhois(self):
-101"""Test that marvin responds to whois"""
-102self.assertStringsOutput(marvin_actions.marvinWhoIs,"vem är marvin?","whois")
-103self.assertActionSilent(marvin_actions.marvinWhoIs,"vemär")
-104
-105deftestGoogle(self):
-106"""Test that marvin can help google stuff"""
-107withmock.patch("marvin_actions.random")asr:
-108r.randint.return_value=1
-109self.assertActionOutput(
-110marvin_actions.marvinGoogle,
-111"kan du googla mos",
-112"LMGTFY https://www.google.se/search?q=mos")
-113self.assertActionOutput(
-114marvin_actions.marvinGoogle,
-115"kan du googla google mos",
-116"LMGTFY https://www.google.se/search?q=google+mos")
-117self.assertActionSilent(marvin_actions.marvinGoogle,"du kan googla")
-118self.assertActionSilent(marvin_actions.marvinGoogle,"gogool")
-119
-120deftestExplainShell(self):
-121"""Test that marvin can explain shell commands"""
-122url="http://explainshell.com/explain?cmd=pwd"
-123self.assertActionOutput(marvin_actions.marvinExplainShell,"explain pwd",url)
-124self.assertActionOutput(marvin_actions.marvinExplainShell,"can you explain pwd",url)
-125self.assertActionOutput(
-126marvin_actions.marvinExplainShell,
-127"förklara pwd|grep -o $user",
-128f"{url}%7Cgrep+-o+%24user")
-129
-130self.assertActionSilent(marvin_actions.marvinExplainShell,"explains")
-131
-132deftestSource(self):
-133"""Test that marvin responds to questions about source code"""
-134self.assertStringsOutput(marvin_actions.marvinSource,"source","source")
-135self.assertStringsOutput(marvin_actions.marvinSource,"källkod","source")
-136self.assertActionSilent(marvin_actions.marvinSource,"opensource")
-137
-138deftestBudord(self):
-139"""Test that marvin knows all the commandments"""
-140forninrange(1,5):
-141self.assertStringsOutput(marvin_actions.marvinBudord,f"budord #{n}","budord",f"#{n}")
-142
-143self.assertStringsOutput(marvin_actions.marvinBudord,"visa stentavla 1","budord","#1")
-144self.assertActionSilent(marvin_actions.marvinBudord,"var är stentavlan?")
-145
-146deftestQuote(self):
-147"""Test that marvin can quote The Hitchhikers Guide to the Galaxy"""
-148withmock.patch("marvin_actions.random")asr:
-149r.randint.return_value=1
-150self.assertStringsOutput(marvin_actions.marvinQuote,"ge os ett citat","hitchhiker",1)
-151self.assertStringsOutput(marvin_actions.marvinQuote,"filosofi","hitchhiker",1)
-152self.assertStringsOutput(marvin_actions.marvinQuote,"filosofera","hitchhiker",1)
-153self.assertActionSilent(marvin_actions.marvinQuote,"noquote")
-154
-155fori,_inenumerate(self.strings.get("hitchhiker")):
-156r.randint.return_value=i
-157self.assertStringsOutput(marvin_actions.marvinQuote,"quote","hitchhiker",i)
-158
-159deftestVideoOfToday(self):
-160"""Test that marvin can link to a different video each day of the week"""
-161withmock.patch("marvin_actions.datetime")asdt:
-162fordinrange(1,8):
-163dt.date.weekday.return_value=d-1
-164day=self.strings.get("weekdays").get(str(d))
-165video=self.strings.get("video-of-today").get(str(d))
-166response=f"{day} En passande video är {video}"
-167self.assertActionOutput(marvin_actions.marvinVideoOfToday,"dagens video",response)
-168self.assertActionSilent(marvin_actions.marvinVideoOfToday,"videoidag")
-169
-170deftestHelp(self):
-171"""Test that marvin can provide a help menu"""
-172self.assertStringsOutput(marvin_actions.marvinHelp,"help","menu")
-173self.assertActionSilent(marvin_actions.marvinHelp,"halp")
-174
-175deftestStats(self):
-176"""Test that marvin can provide a link to the IRC stats page"""
-177self.assertStringsOutput(marvin_actions.marvinStats,"stats","ircstats")
-178self.assertActionSilent(marvin_actions.marvinStats,"statistics")
-179
-180deftestIRCLog(self):
-181"""Test that marvin can provide a link to the IRC log"""
-182self.assertStringsOutput(marvin_actions.marvinIrcLog,"irc","irclog")
-183self.assertActionSilent(marvin_actions.marvinIrcLog,"ircstats")
-184
-185deftestSayHi(self):
-186"""Test that marvin responds to greetings"""
-187withmock.patch("marvin_actions.random")asr:
-188forskey,sinenumerate(self.strings.get("smile")):
-189forhkey,hinenumerate(self.strings.get("hello")):
-190forfkey,finenumerate(self.strings.get("friendly")):
-191r.randint.side_effect=[skey,hkey,fkey]
-192self.assertActionOutput(marvin_actions.marvinSayHi,"hej",f"{s}{h}{f}")
-193self.assertActionSilent(marvin_actions.marvinSayHi,"korsning")
-194
-195deftestLunchLocations(self):
-196"""Test that marvin can provide lunch suggestions for certain places"""
-197locations=["karlskrona","goteborg","angelholm","hassleholm","malmo"]
-198withmock.patch("marvin_actions.random")asr:
-199forlocationinlocations:
-200forindex,placeinenumerate(self.strings.get(f"lunch-{location}")):
-201r.randint.side_effect=[0,index]
-202self.assertActionOutput(
-203marvin_actions.marvinLunch,f"mat {location}",f"Ska vi ta {place}?")
-204r.randint.side_effect=[1,2]
-205self.assertActionOutput(
-206marvin_actions.marvinLunch,"dags att luncha","Jag är lite sugen på Indiska?")
-207self.assertActionSilent(marvin_actions.marvinLunch,"matdags")
-208
-209deftestStrip(self):
-210"""Test that marvin can recommend comics"""
-211messageFormat=self.strings.get("commitstrip").get("message")
-212expected=messageFormat.format(url=self.strings.get("commitstrip").get("url"))
-213self.assertActionOutput(marvin_actions.marvinStrip,"lite strip kanske?",expected)
-214self.assertActionSilent(marvin_actions.marvinStrip,"nostrip")
-215
-216deftestRandomStrip(self):
-217"""Test that marvin can recommend random comics"""
-218messageFormat=self.strings.get("commitstrip").get("message")
-219expected=messageFormat.format(url=self.strings.get("commitstrip").get("urlPage")+"123")
-220withmock.patch("marvin_actions.random")asr:
-221r.randint.return_value=123
-222self.assertActionOutput(marvin_actions.marvinStrip,"random strip kanske?",expected)
-223
-224deftestTimeToBBQ(self):
-225"""Test that marvin knows when the next BBQ is"""
-226self.assertBBQResponse(date(2024,5,17),date(2024,5,17),"today")
-227self.assertBBQResponse(date(2024,5,16),date(2024,5,17),"tomorrow")
-228self.assertBBQResponse(date(2024,5,10),date(2024,5,17),"week")
-229self.assertBBQResponse(date(2024,5,1),date(2024,5,17),"base")
-230self.assertBBQResponse(date(2023,10,17),date(2024,5,17),"eternity")
-231
-232self.assertBBQResponse(date(2024,9,20),date(2024,9,20),"today")
-233self.assertBBQResponse(date(2024,9,19),date(2024,9,20),"tomorrow")
-234self.assertBBQResponse(date(2024,9,13),date(2024,9,20),"week")
-235self.assertBBQResponse(date(2024,9,4),date(2024,9,20),"base")
-236
-237deftestNameDayReaction(self):
-238"""Test that marvin only responds to nameday when asked"""
-239self.assertActionSilent(marvin_actions.marvinNameday,"anything")
-240
-241deftestNameDayRequest(self):
-242"""Test that marvin sends a proper request for nameday info"""
-243withmock.patch("marvin_actions.requests")asr:
-244withmock.patch("marvin_actions.datetime")asd:
-245d.datetime.now.return_value=date(2024,1,2)
-246self.executeAction(marvin_actions.marvinNameday,"namnsdag")
-247self.assertEqual(r.get.call_args.args[0],"http://api.dryg.net/dagar/v2.1/2024/1/2")
-248
-249deftestNameDayResponse(self):
-250"""Test that marvin properly parses nameday responses"""
-251self.assertNameDayOutput("single","Idag har Svea namnsdag")
-252self.assertNameDayOutput("double","Idag har Alfred,Alfrida namnsdag")
-253self.assertNameDayOutput("nobody","Ingen har namnsdag idag")
-254
-255deftestJokeRequest(self):
-256"""Test that marvin sends a proper request for a joke"""
-257withmock.patch("marvin_actions.requests")asr:
-258self.executeAction(marvin_actions.marvinJoke,"joke")
-259self.assertEqual(r.get.call_args.args[0],"https://api.chucknorris.io/jokes/random?category=dev")
-260
-261deftestJoke(self):
-262"""Test that marvin sends a joke when requested"""
-263self.assertJokeOutput("joke","There is no Esc key on Chuck Norris' keyboard, because no one escapes Chuck Norris.")
-264
-265deftestUptime(self):
-266"""Test that marvin can provide the link to the uptime tournament"""
-267self.assertStringsOutput(marvin_actions.marvinUptime,"visa lite uptime","uptime","info")
-268self.assertActionSilent(marvin_actions.marvinUptime,"uptimetävling")
-269
-270deftestStream(self):
-271"""Test that marvin can provide the link to the stream"""
-272self.assertStringsOutput(marvin_actions.marvinStream,"ska mos streama?","stream","info")
-273self.assertActionSilent(marvin_actions.marvinStream,"är mos en streamer?")
-274
-275deftestPrinciple(self):
-276"""Test that marvin can recite some software principles"""
-277principles=self.strings.get("principle")
-278forkey,valueinprinciples.items():
-279self.assertActionOutput(marvin_actions.marvinPrinciple,f"princip {key}",value)
-280withmock.patch("marvin_actions.random")asr:
-281r.choice.return_value="dry"
-282self.assertStringsOutput(marvin_actions.marvinPrinciple,"princip","principle","dry")
-283self.assertActionSilent(marvin_actions.marvinPrinciple,"principlös")
-284
-285deftestCommitRequest(self):
-286"""Test that marvin sends proper requests when generating commit messages"""
-287withmock.patch("marvin_actions.requests")asr:
-288self.executeAction(marvin_actions.marvinCommit,"vad skriver man efter commit -m?")
-289self.assertEqual(r.get.call_args.args[0],"http://whatthecommit.com/index.txt")
-290
-291deftestCommitResponse(self):
-292"""Test that marvin properly handles responses when generating commit messages"""
-293message="Secret sauce #9"
-294response=requests.models.Response()
-295response._content=str.encode(message)
-296withmock.patch("marvin_actions.requests")asr:
-297r.get.return_value=response
-298expected=f"Använd detta meddelandet: '{message}'"
-299self.assertActionOutput(marvin_actions.marvinCommit,"commit",expected)
-300
-301deftestMorning(self):
-302"""Test that marvin wishes good morning, at most once per day"""
-303marvin_general_actions.lastDateGreeted=None
-304withmock.patch("marvin_general_actions.datetime")asd:
-305d.date.today.return_value=date(2024,5,17)
-306withmock.patch("marvin_general_actions.random")asr:
-307r.choice.return_value="Morgon"
-308self.assertActionOutput(marvin_general_actions.marvinMorning,"morrn","Morgon")
-309# Should only greet once per day
-310self.assertActionSilent(marvin_general_actions.marvinMorning,"morgon")
-311# Should greet again tomorrow
-312d.date.today.return_value=date(2024,5,18)
-313self.assertActionOutput(marvin_general_actions.marvinMorning,"godmorgon","Morgon")
-
35defassertActionOutput(self,action,message,expectedOutput):
-36"""Call an action on message and assert expected output"""
-37actualOutput=self.executeAction(action,message)
-38
-39self.assertEqual(actualOutput,expectedOutput)
-
-
-
-
Call an action on message and assert expected output
42defassertActionSilent(self,action,message):
-43"""Call an action with provided message and assert no output"""
-44self.assertActionOutput(action,message,None)
-
-
-
-
Call an action with provided message and assert no output
47defassertStringsOutput(self,action,message,expectedoutputKey,subkey=None):
-48"""Call an action with provided message and assert the output is equal to DB"""
-49expectedOutput=self.strings.get(expectedoutputKey)
-50ifsubkeyisnotNone:
-51ifisinstance(expectedOutput,list):
-52expectedOutput=expectedOutput[subkey]
-53else:
-54expectedOutput=expectedOutput.get(subkey)
-55self.assertActionOutput(action,message,expectedOutput)
-
-
-
-
Call an action with provided message and assert the output is equal to DB
58defassertBBQResponse(self,todaysDate,bbqDate,expectedMessageKey):
-59"""Assert that the proper bbq message is returned, given a date"""
-60url=self.strings.get("barbecue").get("url")
-61message=self.strings.get("barbecue").get(expectedMessageKey)
-62ifisinstance(message,list):
-63message=message[1]
-64ifexpectedMessageKeyin["base","week","eternity"]:
-65message=message%bbqDate
-66
-67withmock.patch("marvin_actions.datetime")asd:
-68d.date.today.return_value=todaysDate
-69withmock.patch("marvin_actions.random")asr:
-70r.randint.return_value=1
-71expected=f"{url}. {message}"
-72self.assertActionOutput(marvin_actions.marvinTimeToBBQ,"dags att grilla",expected)
-
-
-
-
Assert that the proper bbq message is returned, given a date
75defassertNameDayOutput(self,exampleFile,expectedOutput):
-76"""Assert that the proper nameday message is returned, given an inputfile"""
-77withopen(f"namedayFiles/{exampleFile}.json","r",encoding="UTF-8")asf:
-78response=requests.models.Response()
-79response._content=str.encode(json.dumps(json.load(f)))
-80withmock.patch("marvin_actions.requests")asr:
-81r.get.return_value=response
-82self.assertActionOutput(marvin_actions.marvinNameday,"nameday",expectedOutput)
-
-
-
-
Assert that the proper nameday message is returned, given an inputfile
84defassertJokeOutput(self,exampleFile,expectedOutput):
-85"""Assert that a joke is returned, given an input file"""
-86withopen(f"jokeFiles/{exampleFile}.json","r",encoding="UTF-8")asf:
-87response=requests.models.Response()
-88response._content=str.encode(json.dumps(json.load(f)))
-89withmock.patch("marvin_actions.requests")asr:
-90r.get.return_value=response
-91self.assertActionOutput(marvin_actions.marvinJoke,"joke",expectedOutput)
-
-
-
-
Assert that a joke is returned, given an input file
-
-
-
-
-
-
-
-
- def
- testSmile(self):
-
-
-
-
-
-
93deftestSmile(self):
-94"""Test that marvin can smile"""
-95withmock.patch("marvin_actions.random")asr:
-96r.randint.return_value=1
-97self.assertStringsOutput(marvin_actions.marvinSmile,"le lite?","smile",1)
-98self.assertActionSilent(marvin_actions.marvinSmile,"sur idag?")
-
-
-
-
Test that marvin can smile
-
-
-
-
-
-
-
-
- def
- testWhois(self):
-
-
-
-
-
-
100deftestWhois(self):
-101"""Test that marvin responds to whois"""
-102self.assertStringsOutput(marvin_actions.marvinWhoIs,"vem är marvin?","whois")
-103self.assertActionSilent(marvin_actions.marvinWhoIs,"vemär")
-
-
-
-
Test that marvin responds to whois
-
-
-
-
-
-
-
-
- def
- testGoogle(self):
-
-
-
-
-
-
105deftestGoogle(self):
-106"""Test that marvin can help google stuff"""
-107withmock.patch("marvin_actions.random")asr:
-108r.randint.return_value=1
-109self.assertActionOutput(
-110marvin_actions.marvinGoogle,
-111"kan du googla mos",
-112"LMGTFY https://www.google.se/search?q=mos")
-113self.assertActionOutput(
-114marvin_actions.marvinGoogle,
-115"kan du googla google mos",
-116"LMGTFY https://www.google.se/search?q=google+mos")
-117self.assertActionSilent(marvin_actions.marvinGoogle,"du kan googla")
-118self.assertActionSilent(marvin_actions.marvinGoogle,"gogool")
-
-
-
-
Test that marvin can help google stuff
-
-
-
-
-
-
-
-
- def
- testExplainShell(self):
-
-
-
-
-
-
120deftestExplainShell(self):
-121"""Test that marvin can explain shell commands"""
-122url="http://explainshell.com/explain?cmd=pwd"
-123self.assertActionOutput(marvin_actions.marvinExplainShell,"explain pwd",url)
-124self.assertActionOutput(marvin_actions.marvinExplainShell,"can you explain pwd",url)
-125self.assertActionOutput(
-126marvin_actions.marvinExplainShell,
-127"förklara pwd|grep -o $user",
-128f"{url}%7Cgrep+-o+%24user")
-129
-130self.assertActionSilent(marvin_actions.marvinExplainShell,"explains")
-
-
-
-
Test that marvin can explain shell commands
-
-
-
-
-
-
-
-
- def
- testSource(self):
-
-
-
-
-
-
132deftestSource(self):
-133"""Test that marvin responds to questions about source code"""
-134self.assertStringsOutput(marvin_actions.marvinSource,"source","source")
-135self.assertStringsOutput(marvin_actions.marvinSource,"källkod","source")
-136self.assertActionSilent(marvin_actions.marvinSource,"opensource")
-
-
-
-
Test that marvin responds to questions about source code
-
-
-
-
-
-
-
-
- def
- testBudord(self):
-
-
-
-
-
-
138deftestBudord(self):
-139"""Test that marvin knows all the commandments"""
-140forninrange(1,5):
-141self.assertStringsOutput(marvin_actions.marvinBudord,f"budord #{n}","budord",f"#{n}")
-142
-143self.assertStringsOutput(marvin_actions.marvinBudord,"visa stentavla 1","budord","#1")
-144self.assertActionSilent(marvin_actions.marvinBudord,"var är stentavlan?")
-
-
-
-
Test that marvin knows all the commandments
-
-
-
-
-
-
-
-
- def
- testQuote(self):
-
-
-
-
-
-
146deftestQuote(self):
-147"""Test that marvin can quote The Hitchhikers Guide to the Galaxy"""
-148withmock.patch("marvin_actions.random")asr:
-149r.randint.return_value=1
-150self.assertStringsOutput(marvin_actions.marvinQuote,"ge os ett citat","hitchhiker",1)
-151self.assertStringsOutput(marvin_actions.marvinQuote,"filosofi","hitchhiker",1)
-152self.assertStringsOutput(marvin_actions.marvinQuote,"filosofera","hitchhiker",1)
-153self.assertActionSilent(marvin_actions.marvinQuote,"noquote")
-154
-155fori,_inenumerate(self.strings.get("hitchhiker")):
-156r.randint.return_value=i
-157self.assertStringsOutput(marvin_actions.marvinQuote,"quote","hitchhiker",i)
-
-
-
-
Test that marvin can quote The Hitchhikers Guide to the Galaxy
-
-
-
-
-
-
-
-
- def
- testVideoOfToday(self):
-
-
-
-
-
-
159deftestVideoOfToday(self):
-160"""Test that marvin can link to a different video each day of the week"""
-161withmock.patch("marvin_actions.datetime")asdt:
-162fordinrange(1,8):
-163dt.date.weekday.return_value=d-1
-164day=self.strings.get("weekdays").get(str(d))
-165video=self.strings.get("video-of-today").get(str(d))
-166response=f"{day} En passande video är {video}"
-167self.assertActionOutput(marvin_actions.marvinVideoOfToday,"dagens video",response)
-168self.assertActionSilent(marvin_actions.marvinVideoOfToday,"videoidag")
-
-
-
-
Test that marvin can link to a different video each day of the week
-
-
-
-
-
-
-
-
- def
- testHelp(self):
-
-
-
-
-
-
170deftestHelp(self):
-171"""Test that marvin can provide a help menu"""
-172self.assertStringsOutput(marvin_actions.marvinHelp,"help","menu")
-173self.assertActionSilent(marvin_actions.marvinHelp,"halp")
-
-
-
-
Test that marvin can provide a help menu
-
-
-
-
-
-
-
-
- def
- testStats(self):
-
-
-
-
-
-
175deftestStats(self):
-176"""Test that marvin can provide a link to the IRC stats page"""
-177self.assertStringsOutput(marvin_actions.marvinStats,"stats","ircstats")
-178self.assertActionSilent(marvin_actions.marvinStats,"statistics")
-
-
-
-
Test that marvin can provide a link to the IRC stats page
-
-
-
-
-
-
-
-
- def
- testIRCLog(self):
-
-
-
-
-
-
180deftestIRCLog(self):
-181"""Test that marvin can provide a link to the IRC log"""
-182self.assertStringsOutput(marvin_actions.marvinIrcLog,"irc","irclog")
-183self.assertActionSilent(marvin_actions.marvinIrcLog,"ircstats")
-
-
-
-
Test that marvin can provide a link to the IRC log
-
-
-
-
-
-
-
-
- def
- testSayHi(self):
-
-
-
-
-
-
185deftestSayHi(self):
-186"""Test that marvin responds to greetings"""
-187withmock.patch("marvin_actions.random")asr:
-188forskey,sinenumerate(self.strings.get("smile")):
-189forhkey,hinenumerate(self.strings.get("hello")):
-190forfkey,finenumerate(self.strings.get("friendly")):
-191r.randint.side_effect=[skey,hkey,fkey]
-192self.assertActionOutput(marvin_actions.marvinSayHi,"hej",f"{s}{h}{f}")
-193self.assertActionSilent(marvin_actions.marvinSayHi,"korsning")
-
-
-
-
Test that marvin responds to greetings
-
-
-
-
-
-
-
-
- def
- testLunchLocations(self):
-
-
-
-
-
-
195deftestLunchLocations(self):
-196"""Test that marvin can provide lunch suggestions for certain places"""
-197locations=["karlskrona","goteborg","angelholm","hassleholm","malmo"]
-198withmock.patch("marvin_actions.random")asr:
-199forlocationinlocations:
-200forindex,placeinenumerate(self.strings.get(f"lunch-{location}")):
-201r.randint.side_effect=[0,index]
-202self.assertActionOutput(
-203marvin_actions.marvinLunch,f"mat {location}",f"Ska vi ta {place}?")
-204r.randint.side_effect=[1,2]
-205self.assertActionOutput(
-206marvin_actions.marvinLunch,"dags att luncha","Jag är lite sugen på Indiska?")
-207self.assertActionSilent(marvin_actions.marvinLunch,"matdags")
-
-
-
-
Test that marvin can provide lunch suggestions for certain places
-
-
-
-
-
-
-
-
- def
- testStrip(self):
-
-
-
-
-
-
209deftestStrip(self):
-210"""Test that marvin can recommend comics"""
-211messageFormat=self.strings.get("commitstrip").get("message")
-212expected=messageFormat.format(url=self.strings.get("commitstrip").get("url"))
-213self.assertActionOutput(marvin_actions.marvinStrip,"lite strip kanske?",expected)
-214self.assertActionSilent(marvin_actions.marvinStrip,"nostrip")
-
-
-
-
Test that marvin can recommend comics
-
-
-
-
-
-
-
-
- def
- testRandomStrip(self):
-
-
-
-
-
-
216deftestRandomStrip(self):
-217"""Test that marvin can recommend random comics"""
-218messageFormat=self.strings.get("commitstrip").get("message")
-219expected=messageFormat.format(url=self.strings.get("commitstrip").get("urlPage")+"123")
-220withmock.patch("marvin_actions.random")asr:
-221r.randint.return_value=123
-222self.assertActionOutput(marvin_actions.marvinStrip,"random strip kanske?",expected)
-
-
-
-
Test that marvin can recommend random comics
-
-
-
-
-
-
-
-
- def
- testTimeToBBQ(self):
-
-
-
-
-
-
224deftestTimeToBBQ(self):
-225"""Test that marvin knows when the next BBQ is"""
-226self.assertBBQResponse(date(2024,5,17),date(2024,5,17),"today")
-227self.assertBBQResponse(date(2024,5,16),date(2024,5,17),"tomorrow")
-228self.assertBBQResponse(date(2024,5,10),date(2024,5,17),"week")
-229self.assertBBQResponse(date(2024,5,1),date(2024,5,17),"base")
-230self.assertBBQResponse(date(2023,10,17),date(2024,5,17),"eternity")
-231
-232self.assertBBQResponse(date(2024,9,20),date(2024,9,20),"today")
-233self.assertBBQResponse(date(2024,9,19),date(2024,9,20),"tomorrow")
-234self.assertBBQResponse(date(2024,9,13),date(2024,9,20),"week")
-235self.assertBBQResponse(date(2024,9,4),date(2024,9,20),"base")
-
-
-
-
Test that marvin knows when the next BBQ is
-
-
-
-
-
-
-
-
- def
- testNameDayReaction(self):
-
-
-
-
-
-
237deftestNameDayReaction(self):
-238"""Test that marvin only responds to nameday when asked"""
-239self.assertActionSilent(marvin_actions.marvinNameday,"anything")
-
-
-
-
Test that marvin only responds to nameday when asked
-
-
-
-
-
-
-
-
- def
- testNameDayRequest(self):
-
-
-
-
-
-
241deftestNameDayRequest(self):
-242"""Test that marvin sends a proper request for nameday info"""
-243withmock.patch("marvin_actions.requests")asr:
-244withmock.patch("marvin_actions.datetime")asd:
-245d.datetime.now.return_value=date(2024,1,2)
-246self.executeAction(marvin_actions.marvinNameday,"namnsdag")
-247self.assertEqual(r.get.call_args.args[0],"http://api.dryg.net/dagar/v2.1/2024/1/2")
-
-
-
-
Test that marvin sends a proper request for nameday info
-
-
-
-
-
-
-
-
- def
- testNameDayResponse(self):
-
-
-
-
-
-
249deftestNameDayResponse(self):
-250"""Test that marvin properly parses nameday responses"""
-251self.assertNameDayOutput("single","Idag har Svea namnsdag")
-252self.assertNameDayOutput("double","Idag har Alfred,Alfrida namnsdag")
-253self.assertNameDayOutput("nobody","Ingen har namnsdag idag")
-
-
-
-
Test that marvin properly parses nameday responses
-
-
-
-
-
-
-
-
- def
- testJokeRequest(self):
-
-
-
-
-
-
255deftestJokeRequest(self):
-256"""Test that marvin sends a proper request for a joke"""
-257withmock.patch("marvin_actions.requests")asr:
-258self.executeAction(marvin_actions.marvinJoke,"joke")
-259self.assertEqual(r.get.call_args.args[0],"https://api.chucknorris.io/jokes/random?category=dev")
-
-
-
-
Test that marvin sends a proper request for a joke
-
-
-
-
-
-
-
-
- def
- testJoke(self):
-
-
-
-
-
-
261deftestJoke(self):
-262"""Test that marvin sends a joke when requested"""
-263self.assertJokeOutput("joke","There is no Esc key on Chuck Norris' keyboard, because no one escapes Chuck Norris.")
-
-
-
-
Test that marvin sends a joke when requested
-
-
-
-
-
-
-
-
- def
- testUptime(self):
-
-
-
-
-
-
265deftestUptime(self):
-266"""Test that marvin can provide the link to the uptime tournament"""
-267self.assertStringsOutput(marvin_actions.marvinUptime,"visa lite uptime","uptime","info")
-268self.assertActionSilent(marvin_actions.marvinUptime,"uptimetävling")
-
-
-
-
Test that marvin can provide the link to the uptime tournament
-
-
-
-
-
-
-
-
- def
- testStream(self):
-
-
-
-
-
-
270deftestStream(self):
-271"""Test that marvin can provide the link to the stream"""
-272self.assertStringsOutput(marvin_actions.marvinStream,"ska mos streama?","stream","info")
-273self.assertActionSilent(marvin_actions.marvinStream,"är mos en streamer?")
-
-
-
-
Test that marvin can provide the link to the stream
-
-
-
-
-
-
-
-
- def
- testPrinciple(self):
-
-
-
-
-
-
275deftestPrinciple(self):
-276"""Test that marvin can recite some software principles"""
-277principles=self.strings.get("principle")
-278forkey,valueinprinciples.items():
-279self.assertActionOutput(marvin_actions.marvinPrinciple,f"princip {key}",value)
-280withmock.patch("marvin_actions.random")asr:
-281r.choice.return_value="dry"
-282self.assertStringsOutput(marvin_actions.marvinPrinciple,"princip","principle","dry")
-283self.assertActionSilent(marvin_actions.marvinPrinciple,"principlös")
-
-
-
-
Test that marvin can recite some software principles
-
-
-
-
-
-
-
-
- def
- testCommitRequest(self):
-
-
-
-
-
-
285deftestCommitRequest(self):
-286"""Test that marvin sends proper requests when generating commit messages"""
-287withmock.patch("marvin_actions.requests")asr:
-288self.executeAction(marvin_actions.marvinCommit,"vad skriver man efter commit -m?")
-289self.assertEqual(r.get.call_args.args[0],"http://whatthecommit.com/index.txt")
-
-
-
-
Test that marvin sends proper requests when generating commit messages
-
-
-
-
-
-
-
-
- def
- testCommitResponse(self):
-
-
-
-
-
-
291deftestCommitResponse(self):
-292"""Test that marvin properly handles responses when generating commit messages"""
-293message="Secret sauce #9"
-294response=requests.models.Response()
-295response._content=str.encode(message)
-296withmock.patch("marvin_actions.requests")asr:
-297r.get.return_value=response
-298expected=f"Använd detta meddelandet: '{message}'"
-299self.assertActionOutput(marvin_actions.marvinCommit,"commit",expected)
-
-
-
-
Test that marvin properly handles responses when generating commit messages
-
-
-
-
-
-
-
-
- def
- testMorning(self):
-
-
-
-
-
-
301deftestMorning(self):
-302"""Test that marvin wishes good morning, at most once per day"""
-303marvin_general_actions.lastDateGreeted=None
-304withmock.patch("marvin_general_actions.datetime")asd:
-305d.date.today.return_value=date(2024,5,17)
-306withmock.patch("marvin_general_actions.random")asr:
-307r.choice.return_value="Morgon"
-308self.assertActionOutput(marvin_general_actions.marvinMorning,"morrn","Morgon")
-309# Should only greet once per day
-310self.assertActionSilent(marvin_general_actions.marvinMorning,"morgon")
-311# Should greet again tomorrow
-312d.date.today.return_value=date(2024,5,18)
-313self.assertActionOutput(marvin_general_actions.marvinMorning,"godmorgon","Morgon")
-
-
-
-
Test that marvin wishes good morning, at most once per day