1 | #! /usr/bin/env python3 |
||
2 | # -*- coding: utf-8 -*- |
||
3 | |||
4 | """ |
||
5 | An IRC bot that answers random questions, keeps a log from the IRC-chat, |
||
6 | easy to integrate in a webpage and montores a phpBB forum for latest topics |
||
7 | by loggin in to the forum and checking the RSS-feed. |
||
8 | |||
9 | You need to install additional modules. |
||
10 | |||
11 | # Install needed modules in local directory |
||
12 | pip3 install --target modules/ feedparser beautifulsoup4 chardet |
||
13 | |||
14 | Modules in modules/ will be loaded automatically. If you want to use a |
||
15 | different directory you can start the program like this instead: |
||
16 | |||
17 | PYTHONPATH=modules python3 main.py |
||
18 | |||
19 | # To get help |
||
20 | PYTHONPATH=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 |
||
28 | Check out the file 'marvin_config_default.json' on how to configure, instead |
||
29 | of using cli-options. The default configfile is 'marvin_config.json' but you |
||
30 | can change that using cli-options. |
||
31 | |||
32 | # Make own actions |
||
33 | Check the file 'marvin_strings.json' for the file where most of the strings |
||
34 | are defined and check out 'marvin_actions.py' to see how to write your own |
||
35 | actions. Its just a small function. |
||
36 | |||
37 | # Read from incoming |
||
38 | Marvin reads messages from the incoming/ directory, if it exists, and writes |
||
39 | it out the the irc channel. |
||
40 | """ |
||
41 | |||
42 | 1 | import argparse |
|
43 | 1 | import json |
|
44 | 1 | import logging |
|
45 | 1 | import logging.config |
|
46 | 1 | import os |
|
47 | 1 | import sys |
|
48 | |||
49 | 1 | from discord_bot import DiscordBot |
|
50 | 1 | from irc_bot import IrcBot |
|
51 | |||
52 | 1 | import marvin_actions |
|
53 | 1 | import marvin_general_actions |
|
54 | |||
55 | # |
||
56 | # General stuff about this program |
||
57 | # |
||
58 | 1 | PROGRAM = "marvin" |
|
59 | 1 | AUTHOR = "Mikael Roos" |
|
60 | 1 | EMAIL = "[email protected]" |
|
61 | 1 | VERSION = "0.3.0" |
|
62 | 1 | MSG_VERSION = f"{PROGRAM} version {VERSION}." |
|
63 | |||
64 | 1 | LOG = logging.getLogger("main") |
|
65 | |||
66 | |||
67 | 1 | def printVersion(): |
|
68 | """ |
||
69 | Print version information and exit. |
||
70 | """ |
||
71 | 1 | print(MSG_VERSION) |
|
72 | 1 | sys.exit(0) |
|
73 | |||
74 | |||
75 | 1 | def mergeOptionsWithConfigFile(options, configFile): |
|
76 | """ |
||
77 | Read information from config file. |
||
78 | """ |
||
79 | 1 | if os.path.isfile(configFile): |
|
80 | 1 | with open(configFile, encoding="UTF-8") as f: |
|
81 | 1 | data = json.load(f) |
|
82 | |||
83 | 1 | options.update(data) |
|
84 | 1 | res = json.dumps(options, sort_keys=True, indent=4, separators=(',', ': ')) |
|
85 | 1 | LOG.info("Read configuration from config file '%s'.", configFile) |
|
86 | 1 | LOG.info("Current configuration is: %s", res) |
|
87 | else: |
||
88 | LOG.info("Config file '{%s}' is not readable, skipping.", configFile) |
||
89 | |||
90 | 1 | return options |
|
91 | |||
92 | |||
93 | 1 | def parseOptions(options): |
|
94 | """ |
||
95 | Merge default options with incoming options and arguments and return them as a dictionary. |
||
96 | """ |
||
97 | |||
98 | 1 | parser = argparse.ArgumentParser() |
|
99 | 1 | parser.add_argument("protocol", choices=["irc", "discord"], nargs="?", default="irc") |
|
100 | 1 | parser.add_argument("-v", "--version", action="store_true") |
|
101 | 1 | parser.add_argument("--config") |
|
102 | |||
103 | 1 | for key, value in options.items(): |
|
104 | 1 | parser.add_argument(f"--{key}", type=type(value)) |
|
105 | |||
106 | 1 | args = vars(parser.parse_args()) |
|
107 | 1 | if args["version"]: |
|
108 | 1 | printVersion() |
|
109 | 1 | if args["config"]: |
|
110 | 1 | mergeOptionsWithConfigFile(options, args["config"]) |
|
111 | |||
112 | 1 | for parameter in options: |
|
113 | 1 | if args[parameter]: |
|
114 | 1 | options[parameter] = args[parameter] |
|
115 | |||
116 | 1 | res = json.dumps(options, sort_keys=True, indent=4, separators=(',', ': ')) |
|
117 | 1 | LOG.info("Configuration updated after cli options: %s", res) |
|
118 | |||
119 | 1 | return options |
|
120 | |||
121 | |||
122 | 1 | def determineProtocol(): |
|
123 | """Parse the argument to determine what protocol to use""" |
||
124 | 1 | parser = argparse.ArgumentParser() |
|
125 | 1 | parser.add_argument("protocol", choices=["irc", "discord"], nargs="?", default="irc") |
|
126 | 1 | arg, _ = parser.parse_known_args() |
|
127 | 1 | return arg.protocol |
|
128 | |||
129 | |||
130 | 1 | def createBot(protocol): |
|
131 | """Return an instance of a bot with the requested implementation""" |
||
132 | 1 | if protocol == "irc": |
|
133 | 1 | return IrcBot() |
|
134 | 1 | if protocol == "discord": |
|
135 | 1 | return DiscordBot() |
|
136 | 1 | raise ValueError(f"Unsupported protocol: {protocol}") |
|
137 | |||
138 | |||
139 | 1 | def setupLogging(): |
|
140 | """Set up the logging config""" |
||
141 | with open("logging.json", encoding="UTF-8") as f: |
||
142 | config = json.load(f) |
||
143 | logging.config.dictConfig(config) |
||
144 | |||
145 | 1 | def main(): |
|
146 | """ |
||
147 | Main function to carry out the work. |
||
148 | """ |
||
149 | setupLogging() |
||
150 | protocol = determineProtocol() |
||
151 | bot = createBot(protocol) |
||
152 | options = bot.getConfig() |
||
153 | options.update(mergeOptionsWithConfigFile(options, "marvin_config.json")) |
||
154 | config = parseOptions(options) |
||
155 | bot.setConfig(config) |
||
156 | actions = marvin_actions.getAllActions() |
||
157 | general_actions = marvin_general_actions.getAllGeneralActions() |
||
158 | bot.registerActions(actions) |
||
159 | bot.registerGeneralActions(general_actions) |
||
160 | bot.begin() |
||
161 | |||
162 | sys.exit(0) |
||
163 | |||
164 | |||
165 | 1 | if __name__ == "__main__": |
|
166 | main() |
||
167 |