- reworked mercurial repository handling, removed hardcoded values

Sat, 11 Apr 2015 21:02:09 +0300

author
Teemu Piippo <crimsondusk64@gmail.com>
date
Sat, 11 Apr 2015 21:02:09 +0300
changeset 121
ac07779f788d
parent 120
9880bb697149
child 122
f899af683bbe

- reworked mercurial repository handling, removed hardcoded values

hgpoll.py file | annotate | diff | comparison | revisions
mod_admin.py file | annotate | diff | comparison | revisions
mod_hg.py file | annotate | diff | comparison | revisions
mod_util.py file | annotate | diff | comparison | revisions
modulecore.py file | annotate | diff | comparison | revisions
rest.py file | annotate | diff | comparison | revisions
utility.py file | annotate | diff | comparison | revisions
--- a/hgpoll.py	Mon Jan 19 16:42:36 2015 -0500
+++ b/hgpoll.py	Sat Apr 11 21:02:09 2015 +0300
@@ -3,92 +3,139 @@
 import re
 import bt as Bt
 import irc as Irc
-import os
+import os, sqlite3, subprocess
 from datetime import datetime
 from configfile import Config
 import utility
 g_needCommitsTxtRebuild = True
 
+class CommitsDb (object):
+	def __init__(self):
+		needNew = not os.path.isfile ('commits.db')
+		self.db = sqlite3.connect ('commits.db')
+
+		if needNew:
+			self.createNew()
+
+	def create_new (self):
+		self.db.executescript ('''
+			DROP TABLE IF EXISTS COMMITS;
+			DROP TABLE IF EXISTS REPOS;
+			DROP TABLE IF EXISTS REPOCOMMITS;
+			CREATE TABLE IF NOT EXISTS COMMITS
+			(
+				Node        text NOT NULL,
+				Dateversion text NOT NULL,
+				PRIMARY KEY (Node)
+			);
+			CREATE TABLE IF NOT EXISTS REPOS
+			(
+				Name     text NOT NULL,
+				PRIMARY KEY (Name)
+			);
+			CREATE TABLE IF NOT EXISTS REPOCOMMITS
+			(
+				Reponame text,
+				Node     text,
+				FOREIGN KEY (Reponame) REFERENCES REPOS(Name),
+				FOREIGN KEY (Node) REFERENCES COMMITS(Node)
+			);
+		''')
+
+		print 'Building commits.db...'
+		for repo in all_repo_names():
+			print 'Adding commits from %s...' % repo
+
+			data = subprocess.check_output (['hg', '--cwd', repo, 'log', '--template',
+				'{node} {date|hgdate}\n']).splitlines()
+
+			for line in data:
+				changeset, timestamp, tz = line.split(' ')
+				self.add_commit (repo, changeset, int (timestamp))
+
+		self.commit()
+
+	def add_commit (self, repo, changeset, timestamp):
+		dateversion = datetime.utcfromtimestamp (timestamp).strftime ('%y%m%d-%H%M')
+		self.db.execute ('''
+			INSERT OR IGNORE INTO REPOS
+			VALUES (?)
+		''', (repo,))
+
+		self.db.execute ('''
+			INSERT OR IGNORE INTO COMMITS
+			VALUES (?, ?)
+		''', (changeset, dateversion))
+
+		self.db.execute ('''
+			INSERT INTO REPOCOMMITS 
+			VALUES (?, ?)
+		''', (repo, changeset))
+
+	def get_commit_repos (self, node):
+		cursor = self.db.execute ('''
+			SELECT Reponame
+			FROM REPOCOMMITS
+			WHERE Node LIKE ?
+		''', (node + '%',))
+
+		results = cursor.fetchall()
+		return list (set (zip (*results)[0])) if results else []
+
+	def find_commit_by_dateversion (self, dateversion):
+		cursor = self.db.execute ('''
+			SELECT Node
+			FROM COMMITS
+			WHERE Dateversion = ?
+		''', (dateversion,))
+		result = cursor.fetchone()
+		return result[0] if result else None
+
+	def commit(self):
+		self.db.commit()
+
+g_CommitsDb = CommitsDb()
+
 def color_for_repo (repo_name):
 	repo_name = repo_name.lower()
+	repoconfig = Config.get_node ('hg').get_node ('repos')
 
-	if repo_name == 'zandronum':
-		color = 4
-	elif repo_name == 'zandronum-stable':
-		color = 2
-	elif repo_name == 'zandronum-sandbox':
-		color = 7
-	elif repo_name == 'zandronum-sandbox-stable':
-		color = 3
-	elif repo_name == 'zfc9000':
-		color = 6
-	elif repo_name == 'doomseeker':
-		color = 5
-	else:
-		color = 0
-	return color
+	if repoconfig.has_node (repo_name):
+		return repoconfig.get_node(repo_name).get_value ('colorcode', 0)
+
+	return 0
 
 def prettify_bookmarks (bookmarks):
 	return "\0036 [\002%s\002]" % bookmarks
 
-def make_commits_txt():
-	global g_needCommitsTxtRebuild
-
-	if g_needCommitsTxtRebuild == False:
-		return
+def get_repo_info (reponame):
+	reponame = reponame.lower()
+	repoconfig = Config.get_node ('hg').get_node ('repos')
 
-	print 'Building commits.txt...'
-	# Update zandronum-everything
-	repo = hgapi.Repo ('zandronum-everything')
-	repo.hg_command ('pull', '../zandronum-sandbox')
-	repo.hg_command ('pull', '../zandronum-sandbox-stable')
-	data = repo.hg_command ('log', '--template', '{node} {date|hgdate}\n')
-	f = open ('commits.txt', 'w')
+	if repoconfig.has_node (reponame):
+		return repoconfig.get_node (reponame)
 
-	for line in data.split ('\n'):
-		if line == '':
-			continue
-
-		words = line.split (' ')
-		timestamp = int (words[1])
-		f.write ('%s %s\n' % (words[0], datetime.utcfromtimestamp (timestamp).strftime ('%y%m%d-%H%M')))
-
-	f.close()
-	g_needCommitsTxtRebuild = False
+	return None
 
 ' Check if a repository exists '
-def check_repo_exists (repo_name, repo_owner):
+def check_repo_exists (repo_name):
 	print 'Checking that %s exists...' % repo_name
-	repo_url = 'https://bitbucket.org/%s/%s' % (repo_owner, repo_name)
 	zanrepo = hgapi.Repo (repo_name)
 
+	if not os.path.exists (repo_name):
+		os.makedirs (repo_name)
+
 	try:
 		zanrepo.hg_command ('id', '.')
 	except hgapi.hgapi.HgException:
-		# If the repo does not exist, clone it. zandronum-everything can be spawned
-		# off other repos though
-		if repo_name == 'zandronum-everything':
-			if not os.path.exists (repo_name):
-				os.makedirs (repo_name)
-
-			global g_needCommitsTxtRebuild
-			g_needCommitsTxtRebuild = True
-			print 'Init %s' % repo_name
-			zanrepo.hg_command ('init')
-			print 'Cloning zandronum-sandbox into %s' % repo_name
-			zanrepo.hg_command ('pull', '../zandronum-sandbox')
-			print 'Cloning zandronum-sandbox-stable into %s' % repo_name
-			zanrepo.hg_command ('pull', '../zandronum-sandbox-stable')
-			print 'Done'
-			make_commits_txt()
-			return
-
+		# If the repo does not exist, clone it.
+		repo_url = get_repo_info (repo_name).get_value ('url')
 		try:
-			print 'Cloning %s...' % repo_name
+			print ('Cloning %s...' % repo_name)
 			zanrepo.hg_clone (repo_url, repo_name)
-			print 'Cloning done.'
+			print ('Cloning done.')
 		except Exception as e:
-			print 'Unable to clone %s from %s: %s' % (repo_name, repo_url, str (`e`))
+			print ('Unable to clone %s from %s: %s' % (repo_name, repo_url, e))
 			quit (1)
 
 
@@ -98,39 +145,46 @@
 	def __str__ (self):
 		return self.message
 
-def announce_ticket_resolved (ticket_id, commit_node):
+def announce_ticket_resolved (ticket_id, cset):
 	ticket_id = int (ticket_id)
+	reponames = g_CommitsDb.get_commit_repos (cset)
+
+	if not reponames:
+		raise HgProcessError ('Changeset %s does not appear to exist!' % cset)
+
+	for reponame in reponames:
+		repoinfo = get_repo_info (reponame)
+		if not repoinfo:
+			raise HgProcessError ('Unknown repo %s' % reponame)
 
-	try:
-		repo_name = 'zandronum-stable'
-		zanrepo = hgapi.Repo (repo_name)
-		zanrepo.hg_command ('log', '-r', commit_node)
-	except hgapi.hgapi.HgException:
-		try:
-			repo_name = 'zandronum'
-			zanrepo = hgapi.Repo (repo_name)
-			zanrepo.hg_command ('log', '-r', commit_node)
-		except hgapi.hgapi.HgException:
-			raise HgProcessError ('unknown commit %s' % commit_node)
+		if not repoinfo.get_value ('private', default=False):
+			break
+	else:
+		raise HgProcessError ('Changeset %s is only committed to non-published repositories %s' %
+			(cset, reponames))
 
-	repo_url = 'https://bitbucket.org/Torr_Samaho/%s' % repo_name
+	repo = hgapi.Repo (reponame)
+	repo_url = repoinfo.get_value ('url', default=None)
+
+	if not repo_url:
+		raise HgProcessError ('Repo %s has no url!' % reponame)
 
 	# Acquire additional data
-	moredata = get_commit_data (zanrepo, commit_node,
-		'{author|nonempty}\n{date(date, \'%A %d %B %Y %T\')}').split('\n')
+	moredata = get_commit_data (repo, cset,
+		r"{author|nonempty}\n{date(date, '%A %d %B %Y %H:%M:%S')}").split('\n')
 
 	if len (moredata) != 2:
-		raise HgProcessError ('malformed hg data while processing %s' % commit_node)
+		raise HgProcessError ('malformed hg data while processing %s' % cset)
 
 	commit_author = moredata[0]
 	commit_date = moredata[1]
 	commit_email = ""
-	commit_message = zanrepo.hg_command ('log', '-r', commit_node, '--template', '{desc}')
+	commit_message = repo.hg_command ('log', '-r', cset, '--template', '{desc}')
 
 	try:
 		ticket_data = Bt.get_issue (ticket_id)
 	except Exception as e:
-		raise HgProcessError ("error while processing %s: %s" % (commit_node, e))
+		raise HgProcessError ("error while processing %s: %s" % (cset, e))
 
 	# Remove the email address from the author if possible
 	rex = re.compile (r'^(.+) <([^>]+)>$.*')
@@ -139,7 +193,7 @@
 		commit_author = match.group (1)
 		commit_email = match.group (2)
 
-	commit_diffstat = zanrepo.hg_command ('diff', '--change', commit_node, '--stat')
+	commit_diffstat = repo.hg_command ('diff', '--change', cset, '--stat')
 
 	if len(commit_diffstat) > 0:
 		# commit_diffstat = 'Changes in files:\n[code]\n' + commit_diffstat + '\n[/code]'
@@ -156,7 +210,7 @@
 	commit_message = commit_message.replace ("\n", " ")
 
 	message = 'Issue addressed by commit %s: [b][url=%s/commits/%s]%s[/url][/b]' \
-		% (commit_node, repo_url, commit_node, commit_message)
+		% (cset, repo_url, cset, commit_message)
 	message += "\nCommitted by %s on %s\n\n%s" \
 			% (commit_author, commit_date, commit_diffstat)
 
@@ -176,16 +230,7 @@
 
 	# Set target version if not set
 	if not 'target_version' in ticket_data:
-		ticket_data['target_version'] = '1.4' if repo_name == 'zandronum-stable' else '2.0'
-		need_update = True
-	elif (ticket_data['target_version'] == '2.0' or ticket_data['target_version'] == '2.0-beta') \
-	and repo_name == 'zandronum-stable':
-		# Target version was 2.0 but this was just committed to zandronum-stable, adjust
-		ticket_data['target_version'] = '1.4'
-		need_update = True
-	elif ticket_data['target_version'] == '2.0-beta':
-		# Fix target version from 2.0-beta to 2.0
-		ticket_data['target_version'] = '2.0'
+		ticket_data['target_version'] = repoinfo.get_value ('version')
 		need_update = True
 
 	# Announce on IRC
@@ -193,7 +238,7 @@
 		for channel in irc_client.channels:
 			if channel.get_value ('btannounce', default=True):
 				irc_client.privmsg (channel.get_value ('name'),
-					"\003%d%s\003: commit\0035 %s\003 addresses issue\002\0032 %d\002" % (color_for_repo (repo_name), repo_name, commit_node, ticket_id))
+					"\003%d%s\003: commit\0035 %s\003 addresses issue\002\0032 %d\002" % (color_for_repo (reponame), reponame, cset, ticket_id))
 				irc_client.privmsg (channel.get_value ('name'),
 					"Read all about it here: " + Bt.get_ticket_url (ticket_id))
 
@@ -205,19 +250,17 @@
 
 	Bt.post_note (ticket_id, message)
 
+def all_repo_names():
+	return Config.get_node ('hg').get_value ('repos', {}).keys()
+
 def init():
-	check_repo_exists ('zandronum', 'Torr_Samaho')
-	check_repo_exists ('zandronum-stable', 'Torr_Samaho')
-	check_repo_exists ('zandronum-sandbox', 'crimsondusk')
-	check_repo_exists ('zandronum-sandbox-stable', 'crimsondusk')
-	check_repo_exists ('zandronum-everything', '')
-	check_repo_exists ('zfc9000', 'crimsondusk')
-	check_repo_exists ('doomseeker', 'Blzut3')
+	for repo in all_repo_names():
+		check_repo_exists (repo)
 	global repocheck_timeout
-	repocheck_timeout = (time.time()) + 15
+	repocheck_timeout = time.time() + 15
 
-def get_commit_data (zanrepo, rev, template):
-	return zanrepo.hg_command ('log', '-l', '1', '-r', rev, '--template', template)
+def get_commit_data (repo, rev, template):
+	return repo.hg_command ('log', '-l', '1', '-r', rev, '--template', template)
 
 def decipher_hgapi_error (e):
 	# Blah, hgapi, why must your error messages be so mangled?
@@ -251,23 +294,12 @@
 	if time.time() < repocheck_timeout:
 		return
 
-	for n in ['zandronum-stable', 'zandronum', 'zandronum-sandbox', 'zandronum-sandbox-stable', 'zfc9000', 'doomseeker']:
-		poll_one_repo (n)
+	for reponame in all_repo_names():
+		poll_one_repo (reponame)
 
 	hgns = Config.get_node ('hg')
 	repocheck_timeout = time.time() + hgns.get_value ('checkinterval', default=15) * 60
 
-def get_repo_owner (repo_name):
-	usesandbox = repo_name == 'zandronum-sandbox' or repo_name == 'zandronum-sandbox-stable'
-	repo_name = repo_name.lower()
-	if repo_name[:9] == 'zandronum':
-		repo_owner = 'Torr_Samaho' if (not usesandbox) else 'crimsondusk'
-	elif repo_name == 'zfc9000':
-		repo_owner = 'crimsondusk'
-	elif repo_name == 'doomseeker':
-		repo_owner = 'Blzut3'
-	return repo_owner
-
 def poll_one_repo (repo_name):
 	global repocheck_timeout
 	hgns = Config.get_node ('hg')
@@ -275,16 +307,14 @@
 	if not hgns.get_value ('track', default=True):
 		return
 
-	usesandbox = repo_name == 'zandronum-sandbox' or repo_name == 'zandronum-sandbox-stable'
-	repo_owner = get_repo_owner (repo_name)
-	zanrepo = hgapi.Repo (repo_name)
+	repo = hgapi.Repo (repo_name)
 	commit_data = []
 	delimeter = '@@@@@@@@@@'
-	# print 'Checking %s for updates' % repo_name
+	print 'Checking %s for updates' % repo_name
 
 	try:
-		data = zanrepo.hg_command ('incoming', '--quiet', '--template',
-			'{node|short} {desc}' + delimeter)
+		data = repo.hg_command ('incoming', '--quiet', '--template',
+			'{node|short} {desc}' + delimeter).split (delimeter)
 	except hgapi.hgapi.HgException as e:
 		deciphered = decipher_hgapi_error (e)
 
@@ -296,7 +326,10 @@
 		Irc.broadcast ("%s" % `e`)
 		return
 
-	for line in data.split (delimeter):
+	if not data:
+		print ('No updates to %s' % repo_name)
+
+	for line in data:
 		if line == '':
 			continue
 
@@ -319,10 +352,8 @@
 		return
 
 	repo_name = repo_name.lower()
-	usestable = repo_name == 'zandronum-stable'
-	usesandbox = repo_name == 'zandronum-sandbox' or repo_name == 'zandronum-sandbox-stable'
-	repo_owner = get_repo_owner (repo_name)
-	repo_url = 'https://bitbucket.org/%s/%s' % (repo_owner, repo_name)
+	repo_url = get_repo_info (repo_name).get_value ('url')
+	ispublishing = not get_repo_info (repo_name).get_value ('private', False)
 	num_commits = 0
 	zanrepo = hgapi.Repo (repo_name)
 	print '%d new commits on %s' % (len (commit_data), repo_name)
@@ -345,26 +376,6 @@
 	print 'Pulling new commits...'
 	try:
 		zanrepo.hg_command ('pull', *pull_args)
-
-		if repo_name[0:9] == 'zandronum':
-			# Also pull these commits to the zandronum main repository
-			if usestable:
-				devrepo = hgapi.Repo ('zandronum')
-				devrepo.hg_command ('pull', '../zandronum-stable', *pull_args)
-			# Pull everything into sandboxes too
-			if not usesandbox:
-				devrepo = hgapi.Repo ('zandronum-sandbox')
-				devrepo.hg_command ('pull', '../%s' % repo_name, *pull_args)
-	
-				devrepo = hgapi.Repo ('zandronum-sandbox-stable')
-				devrepo.hg_command ('pull', '../%s' % repo_name, *pull_args)
-	
-			devrepo = hgapi.Repo ('zandronum-everything')
-			devrepo.hg_command ('pull', '../%s' % repo_name, *pull_args)
-	
-			global g_needCommitsTxtRebuild
-			g_needCommitsTxtRebuild = True
-
 	except Exception as e:
 		Irc.broadcast ('Warning: unable to pull: %s' % `e`)
 		return
@@ -375,12 +386,24 @@
 		print 'Processing new commit %s...' % commit_node
 
 		try:
-			data = get_commit_data (zanrepo, commit_node, '{author}@@@@@@@@@@{bookmarks}').split ("@@@@@@@@@@")
+			alreadyAdded = len (g_CommitsDb.get_commit_repos (commit_node)) > 0
+
+			delim = '@@@@@@@@@@'
+			data = get_commit_data (zanrepo, commit_node, delim.join (['{author}', '{bookmarks}', \
+				'{date|hgdate}'])).split (delim)
+			print 'data is: %s' % data
 			commit_author = data[0]
 			commit_bookmarks = prettify_bookmarks (data[1])
+			commit_time = int (data[2].split (' ')[0])
 			commit_url = '%s/commits/%s' % (repo_url, commit_node)
 			commit_email = ''
 
+			# If the commit was already in the commits database, it is not a new one and we should
+			# not react to it. Still add it to the db though so that the new repo name is added.
+			g_CommitsDb.add_commit (repo=repo_name, changeset=commit_node, timestamp=commit_time)
+			if alreadyAdded:
+				continue
+
 			# Remove the email address from the author if possible
 			rex = re.compile (r'^(.+) <([^>]+)>$.*')
 			match = rex.match (commit_author)
@@ -396,7 +419,7 @@
 					if not channel.get_value ('btannounce', False):
 						continue
 
-					if (not usesandbox and repo_name != 'zfc9000') or channel.get_value ('btprivate', False):
+					if ispublishing or channel.get_value ('btprivate', False):
 						irc_client.privmsg (channel.get_value ('name'),
 							"\003%d%s\003: new commit\0035 %s%s\003 by\0032 %s\003: %s"
 							% (color_for_repo (repo_name), repo_name, commit_node, commit_bookmarks,
@@ -405,7 +428,7 @@
 						for line in commit_message.split ('\n'):
 							irc_client.privmsg (channel.get_value ('name'), line)
 
-			if not usesandbox:
+			if ispublishing:
 				rex = re.compile (r'^.*(fixes|resolves|addresses|should fix) ([0-9]+).*$')
 				match = rex.match (commit_message)
 
@@ -417,6 +440,8 @@
 			Irc.broadcast ('Error while processing %s: %s' % (commit_node, e))
 			continue
 
+	g_CommitsDb.commit()
+
 def force_poll():
 	global repocheck_timeout
 	repocheck_timeout = 0
--- a/mod_admin.py	Mon Jan 19 16:42:36 2015 -0500
+++ b/mod_admin.py	Sat Apr 11 21:02:09 2015 +0300
@@ -1,5 +1,5 @@
 from modulecore import command_error
-import hgapi
+import sys
 
 ModuleData = {
 	'commands':
@@ -31,6 +31,13 @@
 			'args': None,
 			'level': 'admin',
 		},
+
+		{
+			'name': 'modreload',
+			'description': 'Reloads a module',
+			'args': '<module>',
+			'level': 'admin',
+		},
 	]
 }
 
@@ -45,3 +52,6 @@
 
 def cmd_die (bot, **rest):
 	quit()
+
+def cmd_modreload (bot, args, **rest):
+	reload (sys.modules[args['module']])
\ No newline at end of file
--- a/mod_hg.py	Mon Jan 19 16:42:36 2015 -0500
+++ b/mod_hg.py	Sat Apr 11 21:02:09 2015 +0300
@@ -36,13 +36,6 @@
 			'args': '<ticket> <changeset>',
 			'level': 'admin', # TODO
 		},
-
-		{
-			'name': 'compress',
-			'description': 'Compresses a head on the sandbox repositories.',
-			'args': '<changeset>',
-			'level': 'admin', # TODO
-		}
 	]
 }
 
@@ -52,45 +45,64 @@
 def cmd_checkhg (bot, **rest):
 	HgPoll.force_poll()
 
-def cmd_cset (bot, args, reply, **rest):
-	repo = Repo ('zandronum-everything')
-	data = ""
-	node = args['key']
+def is_dateversion (key):
+	try:
+		datetime.strptime (key, '%y%m%d-%H%M')
+		return True
+	except ValueError:
+		return False
+
+def resolve_node (node):
+	reponame = None
+
+	if '/' in node:
+		reponame, node = node.split ('/')[0:2]
+		
+		if reponame not in HgPoll.all_repo_names():
+			command_error ('''unknown repository %s''' % reponame)
 
 	# Possibly we're passed a date version instead. Try find the node for this.
-	try:
-		datetime.strptime (args['key'], '%y%m%d-%H%M')
-		HgPoll.make_commits_txt()
-		commits_txt = open ('commits.txt', 'r')
+	if is_dateversion (node):
+		node = HgPoll.g_CommitsDb.find_commit_by_dateversion (node)
+
+		if node == None:
+			command_error ('''couldn't find changeset for date %s''' % node)
+			return
+
+		node = node[0:7]
+
+	noderepos = HgPoll.g_CommitsDb.get_commit_repos (node)
 
-		for line in commits_txt:
-			data = line.replace ('\n', '').split (' ')
-			if data[1] == args['key']:
-				node = data[0]
-				break
-		else:
-			command_error ('couldn\'t find changeset for date %s' % args['key'])
-			return
-	except ValueError:
-		pass
+	if reponame == None:
+		if not noderepos:
+			command_error ('''couldn't find changeset %s''' % node)
+
+		reponame = noderepos[0]
+
+	return (node, reponame)
 
-	# zandronum-everything contains all zandronum changesets, so look for changesets in that.
+def cmd_cset (bot, args, reply, **rest):
+	data = ""
+	node, reponame = resolve_node (args['key'])
+	repourl = HgPoll.get_repo_info (reponame).get_value ('url')
+	repo = Repo (reponame)
+	delim = '@@@@@@@@@@@@'
+
 	try:
 		data = repo.hg_command ("log", "-l1", "-r", node, "--template",
-			"{node|short}@@@@@@@" +
-			"{desc}@@@@@@@" +
-			"{author}@@@@@@@" +
-			"{diffstat}@@@@@@@" +
-			"{date|hgdate}@@@@@@@" +
-			"{bookmarks}@@@@@@@" +
-			"{latesttagdistance}@@@@@@@" +
-			"{latesttag}")
+			delim.join (["{node|short}",
+			"{desc}",
+			"{author}",
+			"{diffstat}",
+			"{date|hgdate}",
+			"{bookmarks}",
+			"{latesttagdistance}",
+			"{latesttag}"])).split (delim)
 	except hgapi.HgException:
-		command_error ('couldn\'t find changeset %s' % (node))
+		command_error ('''couldn't find changeset %s in %s''' % (node, reponame))
 		return
 
 	try:
-		data = data.split ('@@@@@@@')
 		node = data[0]
 		message = data[1]
 		author = data[2]
@@ -107,23 +119,26 @@
 
 		# Find out the Zandronum version of this changeset
 		repo.hg_command ('revert', '-r', node, 'src/version.h')
-		zanversion = '<unknown zandronum version>'
+		zanversion = '<unknown version>'
 
-		with open ('zandronum-everything/src/version.h') as version_file:
-			regexps = [ \
-				re.compile (r'#define\s+GAMEVER_STRING\s+"([^"]+)"'), \
-				re.compile (r'#define\s+DOTVERSIONSTR_NOREV\s+"([^"]+)"'), \
-				re.compile (r'#define\s+DOTVERSIONSTR\s+"([^"]+)"')]
+		try:
+			with open (reponame + '/src/version.h') as version_file:
+				regexps = [ \
+					re.compile (r'#define\s+GAMEVER_STRING\s+"([^"]+)"'), \
+					re.compile (r'#define\s+DOTVERSIONSTR_NOREV\s+"([^"]+)"'), \
+					re.compile (r'#define\s+DOTVERSIONSTR\s+"([^"]+)"')]
 
-			for line in version_file:
-				for rex in regexps:
-					match = rex.match (line)
+				for line in version_file:
+					for rex in regexps:
+						match = rex.match (line)
+						if match != None:
+							zanversion = match.group (1)
+							break
+
 					if match != None:
-						zanversion = match.group (1)
 						break
-
-				if match != None:
-					break
+		except IOError:
+			pass
 
 
 		repo.hg_command ('revert', '--all')
@@ -172,6 +187,8 @@
 
 		for line in message.split ('\n'):
 			reply ('    ' + line)
+
+		reply ('url: %s/commits/%s' % (repourl, node))
 	except hgapi.HgException as e:
 		result = HgPoll.decipher_hgapi_error (e)
 
--- a/mod_util.py	Mon Jan 19 16:42:36 2015 -0500
+++ b/mod_util.py	Sat Apr 11 21:02:09 2015 +0300
@@ -5,6 +5,7 @@
 import re
 from modulecore import command_error
 import modulecore as ModuleCore
+import utility
 
 ModuleData = {
 	'commands':
@@ -64,6 +65,13 @@
 			'args': None,
 			'level': 'normal',
 		},
+
+		{
+			'name': 'bitly',
+			'description': 'Shortens a link using bit.ly',
+			'args': '<link>',
+			'level': 'normal'
+		},
 	]
 }
 
@@ -161,7 +169,7 @@
 		expr = mathsubstitute (expr, 'e'  , 2.71828182845904523536028747135266249775724709369995)
 		expr = mathsubstitute (expr, 'phi', 1.6180339887498948482) # golden ratio
 
-		result = subprocess.check_output (['calc', expr], stderr=subprocess.STDOUT) \
+		result = subprocess.check_output (['calc', '--', expr], stderr=subprocess.STDOUT) \
 			.replace ('\t', '') \
 			.replace ('\n', '')
 
@@ -177,11 +185,14 @@
 	except OSError as e:
 		command_error ('failed to execute calc: ' + e.strerror)
 
-def cmd_more (bot, replyto, **rest):
-	ModuleCore.print_responses (bot, replyto)
+def cmd_more (commandObject, **rest):
+	ModuleCore.print_responses (commandObject)
 
 def cmd_yes (**k):
 	ModuleCore.confirm (k, True)
 
 def cmd_no (**k):
 	ModuleCore.confirm (k, False)
+
+def cmd_bitly (reply, args, **k):
+	reply ('Result: %s' % utility.shorten_link (args['link']))
--- a/modulecore.py	Mon Jan 19 16:42:36 2015 -0500
+++ b/modulecore.py	Sat Apr 11 21:02:09 2015 +0300
@@ -162,12 +162,14 @@
 		func (**commandObject)
 	except Confirmxeption as e:
 		if time.time() - g_lastConfirm < 15 and g_confirmCommand != None:
-			command_error ('another confirm is underway')
+			command_error ('''another confirm is underway''')
 
 		g_lastConfirm = time.time()
 		response_function (str (e) + ' (.yes/.no)')
 		commandObject['confirmed'] = e.id
 		g_confirmCommand = commandObject
+	except Exception as e:
+		command_error (str (e))
 
 #
 # call_command (bot, message, cmdname, **kvargs)
@@ -322,9 +324,9 @@
 			arg = arg[0:-3]
 
 		if gotoptional == False:
-			regex += '\s+'
+			regex += r'\s+'
 		else:
-			regex += '\s*'
+			regex += r'\s*'
 
 		if gotliteral:
 			regex += arg
@@ -341,5 +343,8 @@
 		#fi
 	#done
 
+	if not gotvariadic:
+		regex += r'\s*'
+
 	return '^[^ ]+%s$' % regex
 #enddef
--- a/rest.py	Mon Jan 19 16:42:36 2015 -0500
+++ b/rest.py	Sat Apr 11 21:02:09 2015 +0300
@@ -42,11 +42,9 @@
 def throttle (address):
 	tt = datetime.utcnow() + timedelta (0, 30)
 
-	try:
-		# attempt to just update the item
+	if address in g_throttle:
 		g_throttle[g_throttle.index (address)][1] = tt
-	except ValueError:
-		# not in list
+	else:
 		g_throttle.append ([address, tt])
 
 	Irc.broadcast ('Throttling %s for 30 seconds' % address)
@@ -104,8 +102,8 @@
 			with open ('rejected_json.txt', 'w') as fp:
 				fp.write (jsonstring)
 			Irc.broadcast ('bad json written to rejected_json.txt')
-		except:
-			Irc.broadcast ('failed to log json')
+		except Exception as e:
+			Irc.broadcast ('failed to log json because %s' % e)
 			pass
 
 		throttle (address)
--- a/utility.py	Mon Jan 19 16:42:36 2015 -0500
+++ b/utility.py	Sat Apr 11 21:02:09 2015 +0300
@@ -10,6 +10,6 @@
 		try:
 			return c.shorten (link)['url']
 		except Exception as e:
-			Irc.broadcast ('Error while shortening link %s: %s' % (link, e))
+			Irc.broadcast ('Error while shortening link "%s": %s' % (link, e))
 
 	return link

mercurial