forked from sopel-irc/sopel
-
Notifications
You must be signed in to change notification settings - Fork 0
/
willie.py
executable file
·222 lines (196 loc) · 8.63 KB
/
willie.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
#!/usr/bin/env python2.7
# coding=utf8
"""
Willie - An IRC Bot
Copyright 2008, Sean B. Palmer, inamidst.com
Copyright © 2012-2014, Elad Alfassa <[email protected]>
Licensed under the Eiffel Forum License 2.
http://willie.dftba.net
"""
from __future__ import unicode_literals
from __future__ import print_function
import sys
from willie.tools import stderr
if sys.version_info < (2, 7):
stderr('Error: Requires Python 2.7 or later. Try python2.7 willie')
sys.exit(1)
if sys.version_info.major == 3 and sys.version_info.minor < 3:
stderr('Error: When running on Python 3, Python 3.3 is required.')
sys.exit(1)
import os
import argparse
import signal
from willie.__init__ import run, __version__
from willie.config import Config, create_config, ConfigurationError, wizard
import willie.tools as tools
import willie.web
homedir = os.path.join(os.path.expanduser('~'), '.willie')
def enumerate_configs(extension='.cfg'):
configfiles = []
if os.path.isdir(homedir):
willie_dotdirfiles = os.listdir(homedir) # Preferred
for item in willie_dotdirfiles:
if item.endswith(extension):
configfiles.append(item)
return configfiles
def find_config(name, extension='.cfg'):
if os.path.isfile(name):
return name
configs = enumerate_configs(extension)
if name in configs or name + extension in configs:
if name + extension in configs:
name = name + extension
return os.path.join(homedir, name)
def main(argv=None):
global homedir
# Step One: Parse The Command Line
try:
parser = argparse.ArgumentParser(description='Willie IRC Bot',
usage='%(prog)s [options]')
parser.add_argument('-c', '--config', metavar='filename',
help='use a specific configuration file')
parser.add_argument("-d", '--fork', action="store_true",
dest="deamonize", help="Deamonize willie")
parser.add_argument("-q", '--quit', action="store_true", dest="quit",
help="Gracefully quit Willie")
parser.add_argument("-k", '--kill', action="store_true", dest="kill",
help="Kill Willie")
parser.add_argument('--exit-on-error', action="store_true",
dest="exit_on_error", help=(
"Exit immediately on every error instead of "
"trying to recover"))
parser.add_argument("-l", '--list', action="store_true",
dest="list_configs",
help="List all config files found")
parser.add_argument("-m", '--migrate', action="store_true",
dest="migrate_configs",
help="Migrate config files to the new format")
parser.add_argument('--quiet', action="store_true", dest="quiet",
help="Supress all output")
parser.add_argument('-w', '--configure-all', action='store_true',
dest='wizard', help='Run the configuration wizard.')
parser.add_argument('--configure-modules', action='store_true',
dest='mod_wizard', help=(
'Run the configuration wizard, but only for the '
'module configuration options.'))
parser.add_argument('-v', '--version', action="store_true",
dest="version", help="Show version number and exit")
opts = parser.parse_args()
# Step Two: "Do not run as root" checks.
try:
# Linux/Mac
if os.getuid() == 0 or os.geteuid() == 0:
stderr('Error: Do not run Willie with root privileges.')
sys.exit(1)
except AttributeError:
# Windows
if os.environ.get("USERNAME") == "Administrator":
stderr('Error: Do not run Willie as Administrator.')
sys.exit(1)
if opts.version:
py_ver = '%s.%s.%s' % (sys.version_info.major,
sys.version_info.minor,
sys.version_info.micro)
print('Willie %s (running on python %s)' % (__version__, py_ver))
print('http://willie.dftba.net/')
return
elif opts.wizard:
wizard('all', opts.config)
return
elif opts.mod_wizard:
wizard('mod', opts.config)
return
if opts.list_configs:
configs = enumerate_configs()
print('Config files in ~/.willie:')
if len(configs) is 0:
print('\tNone found')
else:
for config in configs:
print('\t%s' % config)
print('-------------------------')
return
config_name = opts.config or 'default'
configpath = find_config(config_name)
if not os.path.isfile(configpath):
print("Welcome to Willie!\nI can't seem to find the configuration file, so let's generate it!\n")
if not configpath.endswith('.cfg'):
configpath = configpath + '.cfg'
create_config(configpath)
configpath = find_config(config_name)
try:
config_module = Config(configpath)
except ConfigurationError as e:
stderr(e)
sys.exit(2)
if config_module.core.not_configured:
stderr('Bot is not configured, can\'t start')
# exit with code 2 to prevent auto restart on fail by systemd
sys.exit(2)
if not config_module.has_option('core', 'homedir'):
config_module.dotdir = homedir
config_module.homedir = homedir
else:
homedir = config_module.core.homedir
config_module.dotdir = config_module.core.homedir
if not config_module.core.logdir:
config_module.core.logdir = os.path.join(homedir, 'logs')
logfile = os.path.os.path.join(config_module.logdir, 'stdio.log')
if not os.path.isdir(config_module.logdir):
os.mkdir(config_module.logdir)
config_module.exit_on_error = opts.exit_on_error
config_module._is_deamonized = opts.deamonize
sys.stderr = tools.OutputRedirect(logfile, True, opts.quiet)
sys.stdout = tools.OutputRedirect(logfile, False, opts.quiet)
# Handle --quit, --kill and saving the PID to file
pid_dir = config_module.core.pid_dir or homedir
if opts.config is None:
pid_file_path = os.path.join(pid_dir, 'willie.pid')
else:
basename = os.path.basename(opts.config)
if basename.endswith('.cfg'):
basename = basename[:-4]
pid_file_path = os.path.join(pid_dir, 'willie-%s.pid' % basename)
if os.path.isfile(pid_file_path):
with open(pid_file_path, 'r') as pid_file:
try:
old_pid = int(pid_file.read())
except ValueError:
old_pid = None
if old_pid is not None and tools.check_pid(old_pid):
if not opts.quit and not opts.kill:
stderr('There\'s already a Willie instance running with this config file')
stderr('Try using the --quit or the --kill options')
sys.exit(1)
elif opts.kill:
stderr('Killing the willie')
os.kill(old_pid, signal.SIGKILL)
sys.exit(0)
elif opts.quit:
stderr('Signaling Willie to stop gracefully')
if hasattr(signal, 'SIGUSR1'):
os.kill(old_pid, signal.SIGUSR1)
else:
os.kill(old_pid, signal.SIGTERM)
sys.exit(0)
elif old_pid is None or (not tools.check_pid(old_pid)
and (opts.kill or opts.quit)):
stderr('Willie is not running!')
sys.exit(1)
elif opts.quit or opts.kill:
stderr('Willie is not running!')
sys.exit(1)
if opts.deamonize:
child_pid = os.fork()
if child_pid is not 0:
sys.exit()
with open(pid_file_path, 'w') as pid_file:
pid_file.write(str(os.getpid()))
config_module.pid_file_path = pid_file_path
# Step Five: Initialise And Run willie
run(config_module)
except KeyboardInterrupt:
print("\n\nInterrupted")
os._exit(1)
if __name__ == '__main__':
main()