1
# -*- coding: utf-8 -*-
2
### This file is part of KoFooBot and is licensed under BSD-license according to the   ###
3
### LICENSE file in the base directory.                                                ###
4
### Code in this file is contributed by:                                               ###
5
###    Krister Svanlund <krister.svanlund gmail.com>                                   ###
6
7
### A module using the MediaWiki API to add text to a wiki.  ###
8
### MediaWiki API: http://www.mediawiki.org/wiki/API         ###
9
### Python API:    http://sourceforge.net/projects/mwclient/ ###
10
11
import mwclient
12
import re
13
14
####
15
#
16
##
17
18
mediawiki_settings = { 'wiki_url': "",
19
                       'wiki_basedir': "/w/",
20
                       'wiki_user': "",
21
                       'wiki_password': ""}
22
23
mediawiki_requires = ['webhelpers']
24
25
mediawiki_helpers = ['mw_add_to_page',
26
                     'mw_get_url',
27
                     'mw_get_table',
28
                     'mw_get_list',
29
                     'mw_get_section_level',
30
                     'mw_get_section',
31
                     'mw_add_table',
32
                     'mw_add_to_table',
33
                     'mw_add_to_list',
34
                     'mw_add_section',
35
                     'mw_rewrite_wiki_links']
36
37
do_wiki_command = { 'description': "Do things to a wiki",
38
                    'long help': """\
39
This is an wide command to do anything the mediawiki module can do with wikipages.
40
Things to take note of is that the 'add to page' and 'add section' command does not add
41
text to the top of the page if at top but instead add it at the bottom of the first
42
section.""",
43
                    'arguments': [("get table|list|section \"<itemname>\" on \"<pagename>\"", "Get a named item from a page on the wiki"),
44
                                  ("get url \"<pagename>\"", "Get URL to a specific page"),
45
                                  ("search \"<term>\"[ in text][ max: <max results>]", "Search titels in wiki for articles"),
46
                                  ("add section \"<sectionname>\" [level <level>|below <parentsection>] on \"<pagename>\" [at top]", "Create a section on a page."),
47
                                  ("add table \"<tablename>\" in \"<parentsection>\" on \"<pagename>\": \"<col1>\"[, \"<col2>\"[..]] [at top]", "Add a table to a section"),
48
                                  ("add to list \"<listname>\" on \"<pagename>\": \"<item>\" [at top]", "Add an item to a list"),
49
                                  ("add to table \"<tablename>\" on \"<pagename>\": \"<col1>\"[, \"<col2>\"[..]] [at top]", "Add a row to a table"),
50
                                  ("add to section \"<sectionname>\" on \"<pagename>\": \"<new text>\" [at top]", "Add text to a section"),
51
                                  ("add to page \"<pagename>\": \"<new text>\" [at top]", "Add text to a page")],
52
                    'public': True,
53
                    'level': 70}
54
55
def init_mediawiki(bot, server, sender = None):
56
    global wiki_site
57
    required_settings = [("Missing wiki url.", bot.settings.mediawiki_wiki_url),
58
                         ("Missing wiki bot-user.", bot.settings.mediawiki_wiki_user),
59
                         ("Missing wiki bot-user password.", bot.settings.mediawiki_wiki_password)]
60
    missing_settings = []
61
    fail = False
62
    for error, setting in required_settings:
63
        if not setting:
64
            print " - %s" % error
65
            if sender:
66
                server.privmsg(sender, error)
67
            fail = True
68
    if fail:
69
        return False
70
    try:
71
        wiki_site = mwclient.Site(bot.settings.mediawiki_wiki_url, bot.settings.mediawiki_wiki_basedir)
72
    except:
73
        print " - Failed to access the site. Could not connect to %s." % bot.settings.mediawiki_wiki_url
74
        if sender:
75
            server.privmsg(sender, "Failed to access the site. Could not connect to %s." % bot.settings.mediawiki_wiki_url)
76
        return False
77
    try:
78
        wiki_site.login(bot.settings.mediawiki_wiki_user, bot.settings.mediawiki_wiki_password)
79
    except Exception, e:
80
        if type(e) is tuple:
81
            result = e[1].get('result', '')
82
            print " - Failed to login to site: %s" % result
83
        else:
84
            print " - Failed to login to site: %s" % str(e)
85
        if sender:
86
            server.privmsg(sender, "Failed to login to the site.")
87
        return False
88
    return True
89
90
#def unload_mediawiki(bot, server, sender = None):
91
#    pass
92
# 
93
#def update_mediawiki(bot, server):
94
#    pass
95
96
def do_wiki(bot, server, sender, target, args):
97
    if args:
98
        if args[0] == 'get' and args[1:]:
99
            ### wiki get table|list|section "itemname" on "pagename" ###
100
            get_pat = re.compile(r'([\"\'])(.*?)\1 (on|from) ([\"\'])(.+)\4')
101
            if args[1] == 'table' and args[2:]:
102
                m = get_pat.match(' '.join(args[2:]))
103
                if m:
104
                    tablename = m.group(2)
105
                    pagename = m.group(5)
106
                    return do_wiki_get_table(bot, server, sender, target, pagename, tablename)
107
            elif args[1] == 'list' and args[2:]:
108
                m = get_pat.match(' '.join(args[2:]))
109
                if m:
110
                    listname = m.group(2)
111
                    pagename = m.group(5)
112
                    return do_wiki_get_list(bot, server, sender, target, pagename, listname)
113
            elif args[1] == 'section' and args[2:]:
114
                m = get_pat.match(' '.join(args[2:]))
115
                if m:
116
                    sectionname = m.group(2)
117
                    pagename = m.group(5)
118
                    return do_wiki_get_section(bot, server, sender, target, pagename, sectionname)
119
            elif args[1] == 'url' and args[2:]:
120
                m = re.match(r'([\"\'])(.*?)\1', ' '.join(args[2:]))
121
                if m:
122
                    pagename = m.group(2)
123
                    return do_wiki_get_url(bot, server, sender, target, pagename)
124
            else:
125
                bot.respond(server, sender, target, "\x02%s\x02 is not a valid argument." % args[1])
126
                return False
127
        elif args[0] == 'add' and args[1:]:
128
            if args[1] == 'section':
129
                return do_wiki_add_section(bot, server, sender, target, args[2:])
130
            elif args[1] == 'table' and args[2:]:
131
                return do_wiki_add_table(bot, server, sender, target, args[2:])
132
            elif args[1] == 'to' and args[2:]:
133
                if args[2] == 'list' and args[3:]:
134
                    return do_wiki_add_to_list(bot, server, sender, target, args[3:])
135
                elif args[2] == 'table' and args[3:]:
136
                    return do_wiki_add_to_table(bot, server, sender, target, args[3:])
137
                elif args[2] == 'section' and args[3:]:
138
                    return do_wiki_add_to_section(bot, server, sender, target, args[3:])
139
                elif args[2] == 'page' and args[3:]:
140
                    return do_wiki_add_to_page(bot, server, sender, target, args[3:])
141
                else:
142
                    bot.respond(server, sender, target, "\x02%s\x02 is not a valid argument or not enough arguments." % args[2])
143
                    return False
144
            else:
145
                bot.respond(server, sender, target, "\x02%s\x02 is not a valid argument or not enough arguments." % args[1])
146
                return False
147
        elif args[0] == 'search' and args[1:]:
148
            m = re.match(r'([\"\'])(.*?)\1(\sin text|)(\smax:?|)\s?([0-9]+|)', ' '.join(args[1:]))
149
            if m and m.group(2).strip():
150
                search_terms = m.group(2)
151
                search_in = ('title', 'text')[len(m.group(3)) > 0]
152
                has_max = len(m.group(4)) > 0
153
                if has_max:
154
                    try:
155
                        result_max = max(int(m.group(5)), 1)
156
                    except Exception, e:
157
                        print " - Could not convert '%s' to integer." % m.group(4)
158
                        result_max = 1
159
                else:
160
                    result_max = 1
161
                if type(search_terms) is not unicode:
162
                    if '\xc3' in search_terms:
163
                        search_terms = search_terms.decode("utf-8")
164
                    else:
165
                        search_terms = search_terms.decode("latin-1")
166
                try:
167
                    results = [r['title'] for r in wiki_site.search(search_terms, what=search_in)]
168
                    if not results:
169
                        print " - No results."
170
                        bot.respond(server, sender, target, "No results.")
171
                        return True
172
                    width = max([len(r.encode("latin-1")) for r in results[:result_max]])
173
                    bot.respond(server, sender, target, "\x02Search results: [{0}/{1}]\x02".format(len(results[:result_max]), len(results)))
174
                    for result in results[:result_max]:
175
                        url = mw_get_url(result)
176
                        if url:
177
                            res_string = " {0:{1}} : {2}".format(result.encode("latin-1"), width, url)
178
                            ### Recode the string from latin-1 to utf-8 ###
179
                            res_string = res_string.decode("latin-1").encode("utf-8")
180
                            bot.respond(server, sender, target, res_string)
181
                        else:
182
                            bot.respond(server, sender, target, " {0}".format(result))
183
                    bot.respond(server, sender, target, "End of results.")
184
                except Exception, e:
185
                    print " - Search failed: %s" % str(e)
186
                    bot.respond(server, sender, target, "Search failed.")
187
                return True
188
            else:
189
                print " - Could not parse arguments."
190
                bot.respond(server, sender, target, "Incorrect search.")
191
                return False
192
        else:
193
            bot.respond(server, sender, target, "\x02%s\x02 is not a valid argument or not enough arguments." % args[0])
194
            return False
195
    else:
196
        bot.respond(server, sender, target, "Not enough arguments.")
197
        return False
198
    
199
##
200
#
201
####
202
203
def do_wiki_get_url(bot, server, sender, target, pagename):
204
    print " + Get URL for '%s'." % pagename
205
    url = mw_get_url(pagename)
206
    if url:
207
        print " + Got url: %s" % url
208
        bot.respond(server, sender, target, url)
209
        return True
210
    else:
211
        print " - Failed to get url."
212
        bot.respond(server, sender, target, "Could not get the url for '%s'." % pagename)
213
        return False
214
    
215
def do_wiki_add_table(bot, server, sender, target, args):
216
    add_table_pat = re.compile(r'([\"\'])(.*?)\1 in ([\"\'])(.*?)\3 on ([\"\'])(.+)\5[:;] ([\"\'])(.+)\7(\sat top|)')
217
    args = ' '.join(args)
218
    print args
219
    m = add_table_pat.match(args)
220
    if m:
221
        print m.groups()
222
        tablename = m.group(2)
223
        sectionname = m.group(4)
224
        pagename = m.group(6)
225
        header_row = '"%s"' % m.group(8)
226
        at_top = (len(m.group(9)) > 0)
227
        print " + At top: %s %s" % (str(at_top), m.group(8))
228
        headers = []
229
        print " + Create table '%s' on '%s'." % (tablename, pagename)
230
        for m in re.finditer(r'\s*"(.*?)"', header_row):
231
            headers.append(m.group(1))
232
        if headers:
233
            if mw_add_table(pagename, tablename, sectionname, headers, not at_top):
234
                bot.respond(server, sender, target, "Successfully added table.")
235
            else:
236
                bot.respond(server, sender, target, "Could not add table to page.")
237
            return True
238
        else:
239
            bot.respond(server, sender, target, "No headers specified.")
240
            return False
241
    else:
242
        bot.respond(server, sender, target, "Could not parse arguments.")
243
        return False
244
    
245
def do_wiki_add_to_table(bot, server, sender, target, args):
246
    add_to_table_pat = re.compile(r'([\"\'])(.*?)\1 on ([\"\'])(.+)\3[:;] (.+)(\sat top|)')
247
    m = add_to_table_pat.match(' '.join(args))
248
    if m:
249
        tablename = m.group(2)
250
        pagename = m.group(4)
251
        new_row = m.group(5)
252
        at_top = len(m.group(6)) > 0
253
        row = []
254
        print " + To table '%s' on '%s'." % (tablename, pagename)
255
        for m in re.finditer(r'\s*"(.*?)"', new_row):
256
            row.append(m.group(1))
257
        if row:
258
            print " + Add: \"%s\"" % '", "'.join(row)
259
            if mw_add_to_table(pagename, tablename, [row], not at_top):
260
                print " + Added row to table."
261
            else:
262
                print " - Failed to add row."
263
            return True
264
        else:
265
            bot.respond(server, sender, target, "Nothing to add.")
266
            return False
267
    else:
268
        bot.respond(server, sender, target, "Could not parse arguments.")
269
        return False
270
    
271
def do_wiki_add_to_list(bot, server, sender, target, args):
272
    add_to_list_pat = re.compile(r'([\"\'])(.*?)\1 on ([\"\'])(.+)\3[:;] ([\"\'])(.*?)\5(\sat top|)')
273
    m = add_to_list_pat.match(' '.join(args))
274
    if m:
275
        listname = m.group(2)
276
        pagename = m.group(4)
277
        new_item = m.group(6)
278
        at_top = len(m.group(7)) > 0
279
        level = 1
280
        if mw_add_to_list(pagename, listname, new_item, level, not at_top):
281
            bot.respond(server, sender, target, "Successfully added new item to list.")
282
            return True
283
        else:
284
            bot.respond(server, sender, target, "Could not add to list.")
285
            return False
286
    else:
287
        bot.respond(server, sender, target, "Could not parse arguments.")
288
        return False
289
290
def do_wiki_add_to_page(bot, server, sender, target, args):
291
    add_to_page_pat = re.compile(r'([\"\'])(.+)\1[:;] ([\"\'])(.*?)\3(\sat top|)')
292
    m = add_to_page_pat.match(' '.join(args))
293
    if m:
294
        pagename = m.group(2)
295
        text_to_add = m.group(4)
296
        at_top = len(m.group(5)) > 0
297
        return mw_add_to_page(pagename, "", text_to_add, "Text added by bot.", not at_top)
298
    else:
299
        bot.respond(server, sender, target, "Could not parse arguments.")
300
        return False
301
    
302
def do_wiki_add_to_section(bot, server, sender, target, args):
303
    add_to_section_pat = re.compile(r'([\"\'])(.*?)\1 on ([\"\'])(.+)\3[:;] ([\"\'])(.*?)\5(\sat top|)')
304
    m = add_to_section_pat.match(' '.join(args))
305
    if m:
306
        sectionname = m.group(2)
307
        pagename = m.group(4)
308
        text_to_add = m.group(6)
309
        at_top = len(m.group(7)) > 0
310
        return mw_add_to_page(pagename, sectionname, text_to_add, "Text added by bot.", not at_top)
311
    else:
312
        bot.respond(server, sender, target, "Could not parse arguments.")
313
        return False
314
    
315
def do_wiki_add_section(bot, server, sender, target, args):
316
    add_section_pat = re.compile(r'([\"\'])(.*?)\1 (to|on) ([\"\'])(.+)\4(\sat top|)')
317
    add_section_level_pat = re.compile(r'([\"\'])(.*?)\1 level ([0-9]+) (to|on) ([\"\'])(.+)\5(\sat top|)')
318
    add_section_parent_pat = re.compile(r'([\"\'])(.*?)\1 below ([\"\'])(.*?)\3 (to|on) ([\"\'])(.+)\6(\sat top|)')
319
    m = add_section_parent_pat.match(' '.join(args))
320
    if m:
321
        sectionname = m.group(2)
322
        sectionlevel = 1
323
        sectionparent = m.group(4)
324
        pagename = m.group(7)
325
        at_top = (len(m.group(8)) > 0)
326
    else:
327
        m = add_section_level_pat.match(' '.join(args))
328
        if m:
329
            sectionname = m.group(2)
330
            try:
331
                sectionlevel = int(m.group(3))
332
            except TypeError:
333
                sectionlevel = 1
334
            sectionparent = ""
335
            pagename = m.group(6)
336
            at_top = (len(m.group(7)) > 0)
337
        else:
338
            m = add_section_pat.match(' '.join(args))
339
            if m:
340
                sectionname = m.group(2)
341
                sectionlevel = 1
342
                sectionparent = ""
343
                pagename = m.group(5)
344
                at_top = (len(m.group(6)) > 0)
345
                print " + At top: %s" % at_top
346
            else:
347
                bot.respond(server, sender, target, "Could not parse arguments.")
348
                return False
349
    if sectionparent:
350
        print " + Add section '%s' below %s on '%s'." % (sectionname, sectionparent, pagename)
351
    else:
352
        print " + Add section '%s' with level %d on '%s'." % (sectionname, sectionlevel, pagename)
353
    success = mw_add_section(pagename, sectionname, sectionlevel, sectionparent, not at_top)
354
    if success:
355
        bot.respond(server, sender, target, "New section created on '%s'." % pagename)
356
    else:
357
        bot.respond(server, sender, target, "Could not create section.")
358
    return True
359
    
360
def do_wiki_get_table(bot, server, sender, target, pagename, tablename):
361
    print "Get '%s' from '%s'." % (tablename, pagename)
362
    table = mw_get_table(pagename, tablename)
363
    if table:
364
        print " + Got table."
365
        table['title'] = mw_rewrite_wiki_links(table.get('title', ""))
366
        table['content'] = [[mw_rewrite_wiki_links(s) for s in r] for r in table.get('content', [])]
367
        table['headers'] = [mw_rewrite_wiki_links(s) for s in table.get('headers', [])]
368
        ret_list = ['\x02\x1F%s\x1F\x02' % table.get('title', '')]
369
        width = {}
370
        for i in xrange(len(table.get('headers', []))):
371
            width[i] = len(table['headers'][i])
372
            for row in table.get('content', []):
373
                if i < len(row):
374
                    width[i] = max(width[i], len(row[i]))
375
        #width = max([max([len(s) for s in r]) for r in table.get('content', [])])
376
        headers = ""
377
        for i, header in zip(xrange(len(table.get('headers', []))), table.get('headers', [])):
378
            headers += " \x02{0:^{1}}\x02 ".format(header.encode("latin-1"), width.get(i, 0))
379
        ret_list.append(headers.decode("latin-1"))
380
        for row in table.get('content', []):
381
            new_row = ""
382
            for i, item in zip(xrange(len(row)), row):
383
                new_row += " {0:{1}} ".format(item.encode("latin-1"), width.get(i, 0))
384
            ret_list.append(new_row.decode("latin-1"))
385
        for s in ret_list:
386
            bot.respond(server, sender, target, bot.convert_to_irc_string(s))
387
        bot.respond(server, sender, target, "End of table.")
388
        print " + Printed all rows to IRC."
389
        return True
390
    else:
391
        bot.respond(server, sender, target, "No table named '%s' found on '%s'." % (tablename, pagename))
392
        return True
393
394
def do_wiki_get_list(bot, server, sender, target, pagename, listname):
395
    print "Get '%s' from '%s'." % (listname, pagename)
396
    xlist = mw_get_list(pagename, listname)
397
    if xlist:
398
        print " + Got list."
399
        ret_list = ['\x02\x1F%s\x1F\x02' % listname]
400
        ret_list.extend([' * %s' % mw_rewrite_wiki_links(s) for s, level in xlist])
401
        ret_list.append('End of list.')
402
        for s in ret_list:
403
            bot.respond(server, sender, target, bot.convert_to_irc_string(s))
404
        print " + Printed all rows to IRC."
405
        return True
406
    else:
407
        bot.respond(server, sender, target, "No list or empty list found.")
408
        return True
409
410
def do_wiki_get_section(bot, server, sender, target, pagename, sectionname):
411
    print "Get '%s' from '%s'." % (sectionname, pagename)
412
    section = mw_get_section(pagename, sectionname)
413
    if section:
414
        print " + Got section."
415
        bot.respond(server, sender, target, "Section \x02'%s\x02:" % sectionname)
416
        for s in section.splitlines():
417
            s = ' '+mw_rewrite_wiki_links(s)
418
            bot.respond(server, sender, target, bot.convert_to_irc_string(s))
419
        bot.respond(server, sender, target, "End of section.")
420
        return True
421
    else:
422
        bot.respond(server, sender, target, "No section with that name.")
423
        return True
424
425
#####
426
# Helper functions after this point
427
##
428
    
429
exp_end_of_section = r'(\n\s*?=+\s*%s\s*=+\s*\n.*)(\n+\s*=+)'
430
exp_start_of_section = r'(\n\s*?=+\s*%s\s*=+\s*)(\n.*?\n)'
431
432
def mw_get_url(pagename):
433
    if type(pagename) is not unicode:
434
        if '\xc3' in pagename:
435
            pagename = pagename.decode("utf-8")
436
        else:
437
            pagename = pagename.decode("latin-1")
438
    import urllib
439
    pagename = urllib.quote(pagename.encode("latin-1"))
440
    norm_title = mwclient.page.Page.normalize_title(pagename)
441
    firstpage = wiki_site.site['base']
442
    page_base = firstpage[:firstpage.rfind('/')+1]
443
    url = page_base+norm_title
444
    return url
445
446
def mw_add_to_page(pagename, parentsection, text_to_add, summary, at_end):
447
    print "Add text to '%s'." % text_to_add
448
    page = wiki_site.Pages[pagename]
449
    text = page.edit()
450
    if parentsection:
451
        ### Get the section, add the text and replace the old section with the new section. ###
452
        section = mw_get_section(pagename, parentsection, text)
453
        if section:
454
            if at_end:
455
                print " + Add text at end of section '%s'." % section
456
                new_section = section+'\n'+text_to_add
457
            else:
458
                print " + Add text at start of section '%s'." % section
459
                new_section = []
460
                added_lines = False
461
                for line in section.splitlines():
462
                    if not added_lines and line:
463
                        m = re.match(r'\s*(=+)(.*?)\1', line)
464
                        if not m:
465
                            print " + Add text."
466
                            new_section.append(text_to_add)
467
                            added_lines = True
468
                        else:
469
                            print " + Found a title: %s" % m.group(2)
470
                    new_section.append(line)
471
                new_section = '\n'.join(new_section)
472
            if new_section:
473
                text = text.replace(section, new_section)
474
                print " + Replace the old section with the new section."
475
    else:
476
        if at_end:
477
            print " + Add text to end of page."
478
            new_text = text+'\n'+text_to_add
479
        else:
480
            print " + Add text before first header in text."
481
            new_text = []
482
            added_lines = False
483
            for line in text.splitlines():
484
                if not added_lines and line:
485
                    m = re.match(r'\s*(=+)(.*?)\1', line)
486
                    if m:
487
                        print " + Found a title: '%s'" % m.group(2)
488
                        new_text.append(text_to_add)
489
                        added_lines = True
490
                new_text.append(line)
491
            if not added_lines:
492
                print " + Text has not been added so add at end of page."
493
                new_text.append(text_to_add)
494
            new_text = '\n'.join(new_text)
495
        if new_text:
496
            text = new_text
497
    try:
498
        page.save(text, summary=summary)
499
        print " + Added text to '%s'." % pagename
500
        return True
501
    except Exception, e:
502
        print " - Failed to save new wiki page: %s" % str(e)
503
        return False
504
505
def mw_add_section(pagename, section, sectionlevel = 1, parent = "", to_bottom = True):
506
    print "Add section '%s' to page '%s'." % (section, pagename)
507
    if pagename and section:
508
        if parent:
509
            parent_level = mw_get_section_level(pagename, parent)
510
        else:
511
            parent_level = 0
512
        if parent and not parent_level:
513
            print " - Failed to find parent level."
514
            return False
515
        section_level = max(parent_level + 1, sectionlevel)
516
        if mw_add_to_page(pagename, parent, "%s %s %s" % ('='*section_level, section, '='*section_level), "Section added by bot.", to_bottom):
517
            print " + Section added."
518
            return True
519
        else:
520
            print " - Failed to add section."
521
            return False
522
    else:
523
        print " - Not enough arguments."
524
        return False
525
526
def mw_add_to_table(pagename, tablename, new_rows, to_bottom = True):
527
    print "Add row to table '%s' on page '%s'." % (tablename, pagename)
528
    if pagename and tablename:
529
        page = wiki_site.Pages[pagename]
530
        text = page.edit()
531
        rows = ""
532
        if type(new_rows) is not list:
533
            new_rows = [new_rows]
534
        for row in new_rows:
535
            if type(row) is not list:
536
                row = [row]
537
            rows += '| %s \n' % ' || '.join(row)
538
        rows = '\n|-\n'.join(rows.splitlines())
539
        for table in re.finditer(r'\{\|.*?\|\}', text, re.S):
540
            old_table = table.group(0)
541
            if re.search(r'\|\+\s*(\'*)%s\1.*?\n' % tablename, old_table):
542
                print " + Has right name."
543
            else:
544
                print " - Not the right name."
545
                continue
546
            new_table = []
547
            row_added = False
548
            has_header = False
549
            for row in old_table.splitlines():
550
                if to_bottom:
551
                    if row.strip()[:2] == '|}':
552
                        new_table.append('|-\n%s\n|}' % rows)
553
                        row_added = True
554
                    else:
555
                        new_table.append(row)
556
                else:
557
                    if row.strip()[:2] == '|+' or row.strip()[0] == '!':
558
                        has_header = True
559
                    if not (row.strip()[:2] == '|+' or row.strip()[0] == '!' or row_added) and row.strip()[0] == '|':
560
                        if has_header:
561
                            new_table.append('|-\n%s' % rows)
562
                        else:
563
                            new_table.append('%s\n|-' % rows)
564
                        row_added = True
565
                    new_table.append(row)
566
            if row_added:
567
                new_table = '\n'.join(new_table)
568
                new_table = re.sub(r'\n\s*\|-.*\n\s*\|-.*\n', r'\n|-\n', new_table)
569
                print " + Successfully added row."
570
                new_text = text.replace(old_table, new_table)
571
                try:
572
                    page.save(new_text, "Added row to table '%s' by bot." % tablename)
573
                    print " + New page is saved."
574
                    return True
575
                except Exception, e:
576
                    print " - Failed to save page: %s", str(e)
577
                    return False
578
            else:
579
                print " - Could not find table on page."
580
                return False
581
    else:
582
        print " - Not enough arguments."
583
        return False
584
585
def create_table(tablename, headers):
586
    table = """\
587
{| style="width:100%%"
588
|+ '''%s'''
589
! %s
590
|-
591
|}""" % (tablename, ' || '.join(headers))
592
    return table
593
    
594
def mw_add_table(pagename, tablename, sectionname, headers, to_bottom):
595
    table = create_table(tablename, headers)
596
    print table
597
    print " + At end: %s" % str(to_bottom)
598
    return mw_add_to_page(pagename, sectionname, table, "Table '%s' added by bot." % tablename, to_bottom)
599
                            
600
def mw_add_to_list(pagename, listname, new_points, level = 1, to_bottom = True):
601
    print "Add point to list '%s' on page '%s'." % (listname, pagename)
602
    if pagename and listname:
603
        page = wiki_site.Pages[pagename]
604
        text = page.edit()
605
        if type(new_points) is not list:
606
            new_points = [new_points]
607
        points = ['%s %s' % ('*'*level, p) for p in new_points] 
608
        section = mw_get_section(pagename, listname, text)
609
        inside_list = False
610
        new_section = []
611
        indent = 0
612
        added_points = False
613
        for line in section.splitlines():
614
            m = None
615
            if line:
616
                print " + Handle string '%s'." % line
617
                m = re.match(r'(\s*)\*(.*)', line)
618
            if m:
619
                print "  + Match point. Inside list: %s." % str(inside_list)
620
                indent = len(m.group(1))
621
                if not inside_list and not to_bottom: ### That is if at top of list ###
622
                    new_section.extend([' '*indent+p for p in points])
623
                    print "   + Add points to top of list."
624
                    print "    + Add: '%s'." % ', '.join(new_points)
625
                    added_points = True
626
                new_section.append(line)
627
                inside_list = True
628
            elif line and inside_list and to_bottom:
629
                inside_list = False
630
                print "  + Add point to bottom of list."
631
                print "   + Add: '%s'." % ', '.join(new_points)
632
                new_section.extend([' '*indent+p for p in points])
633
                new_section.append(line)
634
                added_points = True
635
            else:
636
                new_section.append(line)
637
                inside_list = False
638
        if not added_points:
639
            inside_list = False
640
            print "  + Add point to bottom of section."
641
            print "   + Add: '%s'." % ', '.join(new_points)
642
            new_section.extend([' '*indent+p for p in points])
643
            added_points = True
644
        if added_points:
645
            print " + Points has been added."
646
            new_section = '\n'.join(new_section)
647
            new_text = text.replace(section, new_section)
648
            try:
649
                page.save(new_text, summary = "Added point to list '%s' by bot" % listname)
650
                print " + Saved page."
651
                return True
652
            except Exception, e:
653
                print " - Failed to save page."
654
                return False
655
        else:
656
            print " + No changes has been made."
657
            return False
658
    else:
659
        print " - Not enough arguments."
660
        return False
661
    
662
def mw_get_section_level(pagename, sectionname, text = None):
663
    print "Get section level for section '%s' on %s." % (sectionname, pagename)
664
    if (pagename or text) and sectionname:
665
        if not text:
666
            page = wiki_site.Pages[pagename]
667
            text = page.edit()
668
        m = re.search(r'(=+)\s*%s\s*(=+)' % sectionname, text)
669
        if m:
670
            left = m.group(1)
671
            right = m.group(2)
672
            if left == right:
673
                print " + Got header's level to %d." % len(left)
674
                return len(left)
675
            else:
676
                print " - Header markup missmatch, (%s, %s)." % (left, right)
677
                return 0
678
        else:
679
            print " - No header called '%s'." % sectionname
680
            return 0
681
    else:
682
        print " - Not enough arguments to find level."
683
        return 0
684
685
def mw_get_section(pagename, sectionname, text = None):
686
    print "Get section '%s' from '%s'." % (sectionname, pagename)
687
    if (pagename or text) and sectionname:
688
        if not text:
689
            page = wiki_site.Pages[pagename]
690
            text = page.edit()
691
        section = []
692
        inside_section = False
693
        for line in text.splitlines():
694
            m = re.match(r'\s*(=+)\s*(.*?)\s*\1', line)
695
            if m:
696
                if m.group(2) == sectionname:
697
                    inside_section = True
698
                else:
699
                    inside_section = False
700
            if inside_section:
701
                section.append(line)
702
        section = '\n'.join(section)
703
        if section:
704
            print " + Found section."
705
        else:
706
            print " + Empty section or no section found."
707
        return section
708
    else:
709
        print " - Not enough arguments."
710
        return ""
711
712
def mw_get_list(pagename, listname, text = None):
713
    print "Get list '%s' from '%s'." % (listname, pagename)
714
    if (pagename or text) and listname:
715
        if not text:
716
            page = wiki_site.Pages[pagename]
717
            text = page.edit()
718
        section = mw_get_section(pagename, listname, text)
719
        print section
720
        ret_list = []
721
        inside_list = False
722
        for line in section.splitlines():
723
            m = re.match(r'\s*(\*+)\s*(.*)', line)
724
            if m:
725
                inside_list = True
726
                level = len(m.group(1))
727
                item = m.group(2)
728
                ret_list.append((item, level))
729
            elif inside_list:
730
                inside_list = False
731
                break
732
        if ret_list:
733
            print " + Found list."
734
        else:
735
            print " + No list found or empty list."
736
        return ret_list
737
    else:
738
        print " - Not enough arguments."
739
        return []
740
741
def parse_table(table):
742
    table = re.sub(r'\s*\|\|', r'\n|', table)
743
    new_table = {'title': "",
744
                 'headers': [],
745
                 'content': []}
746
    current_line = ""
747
    for line in table.splitlines():
748
        if line:
749
            if line[:2] == '{|':
750
                print " + Start of table."
751
            elif line[:2] == '|+':
752
                m = re.match(r'\s*(\'*)(.*?)\1', line[2:])
753
                if m:
754
                    title = m.group(2)
755
                    new_table['title'] = title
756
            elif line[:2] == '|-' or line[-2:] == '|}':
757
                current_line_list = [l.strip() for l in current_line.strip()[1:].split(' | ')]
758
                if current_line.strip()[0] == '|':
759
                    new_table['content'].append(current_line_list)
760
                elif current_line.strip()[0] == '!':
761
                    new_table['headers'] = current_line_list
762
                current_line = ""
763
                if line[-2:] == '|}':
764
                    print " + End of table."
765
                    return new_table
766
            elif line[0] == '|':
767
                current_line += ' '+line
768
            elif line[0] == '!':
769
                current_line = line
770
        else:
771
            print " + Empty line."
772
    return None
773
    
774
def mw_get_table(pagename, tablename, text = None):
775
    print "Get table '%s' from '%s'." % (tablename, pagename)
776
    if (pagename or text) and tablename:
777
        if not text:
778
            page = wiki_site.Pages[pagename]
779
            text = page.edit()
780
        for table in re.finditer(r'\{\|.*?\|\}', text, re.S):
781
            table_parsed = parse_table(table.group(0))
782
            if table_parsed:
783
                if table_parsed.get('title') == tablename:
784
                    print " + Title found: %s" % table_parsed.get('title')
785
                    print " + Headers found: %s" % str(table_parsed.get('headers'))
786
                    print " + Content found: %s" % str(table_parsed.get('content'))
787
                    return table_parsed
788
            else:
789
                print " - Failed to parse table."
790
                return None
791
        print " - Could not find a table with that name."
792
        return None
793
    else:
794
        print " - Not enough arguments."
795
        return None
796
           
797
def mw_rewrite_wiki_links(text):
798
    for outer in re.finditer(r'\[(\[?.*?\]?)\]', text):
799
        inner = re.match('\[(.*?)\]', outer.group(1))
800
        if inner:
801
            matches = inner.group(1).split('|')
802
            if len(matches) > 1:
803
                text = text.replace(outer.group(0), matches[-1])
804
            else:
805
                text = text.replace(outer.group(0), matches[0])
806
        else:
807
            matches = outer.group(1).split()
808
            if len(matches) > 1:
809
                text = text.replace(outer.group(0), ' '.join(matches[1:]))
810
            else:
811
                text = text.replace(outer.group(0), matches[0])
812
    return text