aboutsummaryrefslogtreecommitdiffstats
path: root/IRC/reaktor.py
blob: 124fa017c4e96b879715188ceeb5d56ee1b49a66 (plain)
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
#!/usr/bin/env python3
import os
from ircasy import asybot
from asyncore import loop
from translate_colors import translate_colors
import shlex
from re import split, search, match

default_config = './config.py'
from getconf import make_getconf
getconf = None

import logging,logging.handlers
log = logging.getLogger('asybot')
#hdlr = logging.handlers.SysLogHandler(address='/dev/log', facility=logging.handlers.SysLogHandler.LOG_DAEMON)
#formatter = logging.Formatter( '%(filename)s: %(levelname)s: %(message)s')
#hdlr.setFormatter(formatter)
#log.addHandler(hdlr)



class Reaktor(asybot):
  def __init__(self,config=default_config):
    self.config = config
    log.info("using config file %s"%(config))
    asybot.__init__(self, getconf('irc_server'), getconf('irc_port'), getconf('irc_nickname'), getconf('irc_channels'), hammer_interval=getconf('irc_hammer_interval'), alarm_timeout=getconf('irc_alarm_timeout'), kill_timeout=getconf('irc_kill_timeout'))

  def is_admin(self,prefix):
    try:
      with open(getconf('auth_file')) as f:
        for line in f:
          if line.strip() == prefix:
            return True
    except Exception as e:
      log.info(e)
    return False

  def on_join(self, prefix, command, params, rest):
    for command in getconf('on_join', []):
      self.execute_command(command, None, prefix, params)

  def on_ping(self, prefix, command, params, rest):
    for command in getconf('on_ping', []):
      prefix = '!' # => env = { _prefix: '!', _from: '' }
      params = command.get('targets') # TODO why don't we get a list here and use ','.join() ?
      self.execute_command(command, None, prefix, params)

  def on_privmsg(self, prefix, command, params, rest):
    for command in getconf('commands'):
      y = match(command['pattern'], rest)
      if y:
        if not self.is_admin(prefix):
          self.PRIVMSG(params,'unauthorized!')
        else:
          return self.execute_command(command, y, prefix, params)

    for command in getconf('public_commands'):
      y = match(command['pattern'], rest)
      if y:
        return self.execute_command(command, y, prefix, params)


  def execute_command(self, command, match, prefix, target):
    from os.path import realpath, dirname, join
    from subprocess import Popen as popen, PIPE
    from time import time

    #TODO: allow only commands below ./commands/
    exe = join(dirname(realpath(dirname(__file__))), command['argv'][0])
    myargv = [exe] + command['argv'][1:]
    try:
      if match and match.groupdict().get('args', None):
        myargv += shlex.split(match.groupdict()['args'])
    except:
        log.info("cannot parse args!")

    cwd = getconf('workdir')
    if not os.access(cwd,os.W_OK):
        log.error("Workdir '%s' is not Writable! Falling back to root dir"%cwd)
        cwd = "/"

    env = command.get('env', {})
    env['_prefix'] = prefix
    env['_from'] = prefix.split('!', 1)[0]

    env.update(os.environ)
    log.debug('self:' +self.nickname)
    # when receiving /query, answer to the user, not to self
    if self.nickname in target:
      target.remove(self.nickname)
      target.append(env['_from'])
    log.debug('target:' +str(target))

    start = time()
    try:
      log.debug("Running : %s"%str(myargv))
      log.debug("Environ : %s"%(str(env)))
      p = popen(myargv, bufsize=1, stdout=PIPE, stderr=PIPE, env=env, cwd=cwd)
    except Exception as error:
      self.ME(target, 'brain damaged')
      log.error('OSError@%s: %s' % (myargv, error))
      return
    pid = p.pid
    for line in iter(p.stdout.readline, ''.encode()):
      try:
        self.PRIVMSG(target, translate_colors(line.decode()))
      except Exception as error:
        log.error('no send: %s' % error)
      log.debug('%s stdout: %s' % (pid, line))
    p.wait()
    elapsed = time() - start
    code = p.returncode
    log.info('command: %s -> %s in %d seconds' % (myargv, code, elapsed))
    [log.debug('%s stderr: %s' % (pid, x)) for x in p.stderr.readlines()]

    if code != 0:
      self.ME(target, 'mimimi')

if __name__ == "__main__":
  import sys
  conf = sys.argv[1] if len(sys.argv) == 2 else default_config
  getconf = make_getconf(conf)
  logging.basicConfig(level = logging.DEBUG if getconf('debug') else logging.INFO)
  Reaktor(conf)
  loop()