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
|
#!/usr/bin/env python
import git, os, sys
from optparse import OptionParser
from twisted.words.protocols import irc
from twisted.internet import reactor, protocol
from twisted.internet.task import LoopingCall
parser = OptionParser()
parser.add_option('-s', '--host')
parser.add_option('-n', '--nick', default = 'git')
parser.add_option('-d', '--dir')
parser.add_option('-c', '--channel')
parser.add_option('-i', '--interval', type = 'int', default = 30)
(options, args) = parser.parse_args()
if not options.host or not options.dir or not options.channel or not options.nick:
parser.print_help()
sys.exit(1)
root = options.dir
repos = None
def repo_commit_msg(repo, commit):
stat = '%d files,' % commit.stats.total['files']
stat += ' \00305--%d\017' % commit.stats.total['deletions']
stat += ' \00303++%d\017' % commit.stats.total['insertions']
msg = '\002%s\002 pushed to \002%s\002 by \002%s\002 (%s) %s' % (
os.path.splitext(os.path.basename(repo.path))[0],
commit.id_abbrev,
commit.committer.name if commit.author.name == commit.committer.name else '%s/%s' % (commit.committer.name, commit.author.name),
stat,
commit.summary
)
return msg
def check_repos(bot):
global repos
repo_paths = [x for x in os.listdir(root) if x.endswith('.git')]
if not repos:
repos = [[git.Repo('%s/%s' % (root, x)), None] for x in repo_paths]
bot.gitmsg('Repos initialized: %s' % (', '.join([os.path.splitext(os.path.basename(x[0].path))[0] for x in repos])))
else:
old_paths = [os.path.basename(x[0].path) for x in repos]
new_paths = [x for x in repo_paths if not x in old_paths]
del_paths = [x for x in old_paths if not x in repo_paths]
del_repos = [x for x in repos if os.path.basename(x[0].path) in del_paths]
if del_repos:
bot.gitmsg('Removed repo%s: %s' % ('' if len(del_paths) == 1 else '', ', '.join([os.path.splitext(x)[0] for x in del_paths])))
for i in del_repos:
repos.remove(i)
new_added = []
for i in new_paths:
try:
r = git.Repo('%s/%s' % (root, i))
repos.append([r, None])
new_added.append(i)
except:
pass
if new_added:
bot.gitmsg('New repo%s: %s' % ('' if len(new_added) == 1 else 's', ', '.join([os.path.splitext(x)[0] for x in new_added])))
for i, v in enumerate(repos):
repo = v[0]
if not repo.heads: # No commits
continue
last = v[1]
if last == None:
last = repo.heads[0].commit.id
repos[i][1] = last
nlast = repo.heads[0].commit
if last and nlast.id != last:
msg = repo_commit_msg(repo, nlast)
bot.gitmsg(msg)
repos[i][1] = nlast.id
class Bot(irc.IRCClient):
nickname = options.nick
def gitmsg(self, msg):
self.say(options.channel, msg)
def signedOn(self):
self.join(options.channel)
self.repeater = LoopingCall(check_repos, self)
self.repeater.start(options.interval)
def privmsg(self, user, channel, message):
private = channel == self.nickname
nick = user.split('!')[0]
target = nick if private else channel
messagelist = message.split()
if len(messagelist) < 2:
return
if messagelist[0].startswith(self.nickname):
cmd = messagelist[1].lower()
if cmd == 'list':
s = 'Repos: %s' % ', '.join([os.path.splitext(os.path.basename(x[0].path))[0] for x in repos])
self.msg(target, s)
elif cmd == 'last':
repo = messagelist[2].lower() if len(messagelist) > 2 else None
if not repo:
self.msg(target, 'Which repo?')
return
repo = [r for r in repos if os.path.basename(r[0].path).startswith(repo)]
if len(repo) == 1:
r = repo[0][0]
msg = repo_commit_msg(r, r.heads[0].commit)
self.msg(target, msg)
elif len(repo) == 0:
self.msg(target, 'No repo found.')
else:
self.msg(target, 'Ambiguous name: %s' % (', '.join([os.path.basename(x[0].path) for x in repos])))
class BotFactory(protocol.ReconnectingClientFactory):
protocol = Bot
if __name__ == '__main__':
f = BotFactory()
reactor.connectTCP(options.host, 6667, f)
reactor.run()
|