1 """
2 B{emma}: bot for digital assembly
3
4 The code is organice on three layers:
5 1. core controls event passing, config file, threads, mutex, storage, ...
6 2. L{interface} connect emma to the world (email, irc, wiki, microblog,
7 ...)
8 3. L{module} where the actual functionality happens. Easy to program, with
9 simple API of events to intercomunicate with the interfaces.
10
11 @copyright: (c) 2011 hackmeeting U{http://sindominio.net/hackmeeting}
12 @author: Ruben Pollan
13 @organization: hackmeeting U{http://sindominio.net/hackmeeting}
14 @contact: meskio@sindominio.net
15 @license:
16 This program is free software; you can redistribute it and/or
17 modify it under the terms of the Do What The Fuck You Want To
18 Public License, Version 2, as published by Sam Hocevar. See
19 U{http://sam.zoy.org/projects/COPYING.WTFPL} for more details.
20 """
21
22 import os
23 import gettext
24 import thread
25 import ConfigParser
26 import re
27 import logging
28 from time import sleep
29 from cPickle import loads
30
31 from database import DB
32 from sched import at
33 from events import Event
34
35
36 __version__ = 0.2
37 """ Version of emma """
38
39 confPaths = (
40 '/usr/local/etc/emma.cfg',
41 '/etc/emma.cfg',
42 os.path.expanduser('~/.emma.cfg'),
43 'emma.cfg',
44 )
45 """ Configuration file is searched in those locations. The latter files
46 have precedence when a configuration option is set in multiple files.
47 """
48
49
50 -def main(conf_paths=confPaths):
51 """
52 Main function of the program
53
54 It loads all the interfaces and modules described on the config files.
55 """
56 localedir = os.path.join(os.path.dirname(__file__), 'locale')
57 conf = ConfigParser.RawConfigParser()
58 conf.read(conf_paths)
59 _init_log(conf)
60 _init_i18n(conf, localedir)
61 db = DB()
62 db.connect(conf.get("core", "db_host"),
63 int(conf.get("core", "db_port")),
64 conf.get("core", "db_name"))
65 core = db.core()
66 _load_complements(conf)
67 _restore_sched(core)
68
69 while 1:
70 sleep(1000)
71
72
74 try:
75 language = conf.get("core", "language")
76 lang = gettext.translation('emma', localedir,
77 languages=language.split(','))
78 lang.install()
79 except (ConfigParser.NoOptionError, IOError):
80
81
82 gettext.install('emma', localedir)
83
84
86 fmt = "%(asctime)s (%(levelname).1s) %(message)s"
87 datefmt = "%b %d %H:%M:%S"
88
89 levels = {"debug": logging.DEBUG,
90 "info": logging.INFO,
91 "warning": logging.WARNING,
92 "error": logging.ERROR,
93 "critical": logging.CRITICAL}
94 try:
95 strlevel = conf.get("core", "log_level")
96 except ConfigParser.NoOptionError:
97 strlevel = "warning"
98 if strlevel in levels:
99 level = levels[strlevel]
100 else:
101 level = logging.WARNING
102
103 try:
104 log_file = conf.get("core", "log_file")
105 logging.basicConfig(format=fmt, datefmt=datefmt, level=level, \
106 filename=log_file)
107 except ConfigParser.NoOptionError:
108 logging.basicConfig(format=fmt, datefmt=datefmt, level=level)
109
110 if strlevel not in levels:
111 logging.warning(_("[core] Not valid config value on log_level"))
112
113
115 logging.info(_("[core] preparing interfaces and modules"))
116 sectexp = re.compile(r"^[IM] ([^ ]*) (.*)$")
117
118 for section in conf.sections():
119 options = dict(conf.items(section))
120 m = sectexp.match(section)
121 if not m:
122 continue
123 name, identifier = m.groups()
124 if section[0] == 'M':
125 m = _init_complement('module', name, identifier, options)
126 thread.start_new_thread(m.run, ())
127 if section[0] == 'I':
128 i = _init_complement('interface', name, identifier, options)
129 thread.start_new_thread(i.run, ())
130
131
133 db = DB()
134 logging.debug(_("[core] load %(type)s %(name)s") % {'type': tpe,
135 'name': name})
136 db_coll = db.collection("%s_%s_%s" % (tpe, name, identifier))
137 imp = __import__("emma.%s.%s" % (tpe, name))
138 complements = getattr(imp, tpe)
139 complement = getattr(complements, name)
140 clss = getattr(complement, name)
141 return clss(identifier, options, db_coll)
142
143
145 sched = db.find({'element': 'sched', 'type': 'at'})
146 logging.info(_("[core] restore %s scheduled events") % sched.count())
147 for s in sched:
148 elements = loads(str(s['event']))
149 event = Event(*elements)
150 data = loads(str(s['data']))
151 date = s['date']
152 doc_id = s['_id']
153 at(event, data, date, doc_id)
154