OSDN Git Service

page関数に変換ミスがあったのを訂正
[pybbs/pybbs.git] / pybbs.py
1 # -*- coding:utf-8 -*-
2 import os,re,glob
3 from tornado import escape,web,ioloop,httpserver,httpclient
4 import pymongo, urllib
5 from datetime import datetime
6 import json
7 from bson.objectid import ObjectId #don't remove
8 from linebot.api import LineBotApi
9 from linebot.exceptions import (InvalidSignatureError)
10 from linebot.models import (TextSendMessage)
11
12
13 class BaseHandler(web.RequestHandler):
14     def get_current_user(self):
15         user = self.get_secure_cookie('admin_user')
16         return escape.utf8(user)
17     
18     def set_current_user(self,username):
19         self.set_secure_cookie('admin_user',username)
20         
21     def clear_current_user(self):
22         self.clear_cookie('admin_user')
23
24 class IndexHandler(BaseHandler):
25     def get(self,dbname,page='0'):
26         dbname = escape.url_unescape(dbname)
27         params = self.application.db['params'].find_one({'app':'bbs'})
28         if params['mentenance'] is True:
29             self.render('mentenance.htm',title=params['title'],db=dbname)
30             return
31         if self.application.collection(dbname) is False:
32             if self.current_user == b'admin':
33                 coll = self.application.db[dbname]
34                 coll.insert({})
35                 coll.remove({})
36             else:
37                 raise web.HTTPError(404)
38         key = self.get_argument('key','')
39         if key != '':
40             table = self.application.db[dbname]
41             rec = table.find_one({'number':int(key)})
42             if rec:
43                 self.render('article.htm',record=rec)
44                 return
45             else:
46                 raise web.HTTPError(404)
47         i = params['count']      
48         rule = escape.url_unescape(self.get_cookie('aikotoba',''))
49         na = escape.url_unescape(self.get_cookie('username',u'誰かさん'))
50         pos = self.application.gpos(dbname,page)
51         table = self.application.db[dbname]
52         start = (pos-1)*i
53         if start < 0:
54             start = table.count()-i
55             if start < 0:
56                 start = 0
57         rec = table.find()
58         bool = (dbname == params['info name']) 
59         if bool is True:
60             rec.sort('number',-1)
61         else:
62             rec.sort('number')
63         rec.skip(start).limit(i)
64         if table.count() >= 10*i:
65             self.render('modules/full.htm',position=pos,records=rec,data=params,db=dbname)
66             return
67         if bool is True and self.current_user != b'admin':
68             self.render('modules/info.htm',position=pos,records=rec,data=params,db=dbname)
69         else:
70             self.render('modules/index.htm',position=pos,records=rec,data=params,username=na,db=dbname,aikotoba=rule)
71
72 class LoginHandler(BaseHandler):
73     def get(self):
74         info = self.application.db['params'].find_one({'app':'bbs'})
75         query = self.get_query_argument('next','/'+info['info name'])
76         i = query[1:].find('/')
77         if i == -1:
78             qs = query[1:]
79         else:
80             qs = query[1:i+1]
81         self.render('login.htm',db=escape.url_unescape(qs))
82         
83     def post(self):
84         dbname = self.get_argument('record','')
85         if dbname == '':
86             self.redirect('/login')
87             return
88         pw = self.application.db['params'].find_one({'app':'bbs'})
89         if self.get_argument('password') == pw['password']:
90             self.set_current_user('admin')
91         if dbname == 'master':
92             self.redirect('/master')
93         else:
94             self.redirect('/'+dbname+'/admin/0/')
95         
96 class LogoutHandler(BaseHandler):
97     def get(self):
98         self.clear_current_user()
99         self.redirect('/login')
100  
101 class JumpHandler(BaseHandler):
102     def get(self):
103         self.clear_current_user()
104         self.redirect('/')
105         
106 class NaviHandler(web.RequestHandler):
107     def get(self):
108         if self.application.collection('params') is False:
109             item = {"mentenance":False,"out_words":[u"阿保",u"馬鹿",u"死ね"],"password":"admin",
110                     "title2":"<h1 style=color:gray;text-align:center>pybbs</h1>",
111                     "bad_words":["<style","<link","<script","<img"],"count":30,
112                     "title":"pybbs","info name":"info",'app':'bbs'}       
113             self.application.db['params'].insert(item)
114             self.application.db['info']
115         table = self.application.db['params'].find_one({'app':'bbs'})
116         if table['mentenance'] is True:
117             self.render('mentenance.htm',title=table['title'],db=table['info name'])
118             return
119         coll,na = self.name()                
120         self.render('top.htm',coll=coll,name=na,full=self.full)
121                   
122     def name(self):
123         coll = sorted(self.application.coll(),key=str.lower)
124         na = self.application.db['params'].find_one({'app':'bbs'})['info name']
125         if na in coll:
126             coll.remove(na)
127         else:
128             na = ''
129         return coll,na
130             
131     def full(self,dbname):
132         if dbname in self.application.db.collection_names():
133             i = 10*self.application.db['params'].find_one({'app':'bbs'})['count']
134             table = self.application.db[dbname]
135             if table.count() >= i:
136                 return True
137         return False
138
139 class TitleHandler(NaviHandler):
140     def get(self):
141         rec = sorted(self.title(),key=lambda x: x['date2'])
142         self.render('title.htm',coll=rec,full=self.full)  
143         
144     def title(self):
145         name = self.application.coll()
146         for x in name:
147             item = {}
148             item['name'] = x
149             table = self.application.db[x]
150             i = table.count()
151             item['count'] = i            
152             tmp = table.find_one({'number':1})
153             if tmp:
154                 s = tmp['title']
155             else:
156                 s = ''
157             item['title'] = s   
158             if i == 0:
159                 item['date'] = ''
160                 item['date2'] = 0
161             else:
162                 rec = table.find().sort('number')
163                 s = rec[i-1]['date']
164                 item['date'] = s
165                 i = datetime.strptime(s,'%Y/%m/%d %H:%M')
166                 year = datetime.now().year-i.year
167                 if year == 0:
168                     j = 800
169                 elif year == 1:
170                     j = 400
171                 else:
172                     j = 0
173                 item['date2'] = j+31*(i.month-1)+i.day
174             yield item
175         
176 class RegistHandler(web.RequestHandler):
177     def post(self,dbname):
178         if self.application.collection(dbname) is False:
179             raise web.HTTPError(404)
180         self.database = dbname
181         rec = self.application.db['params'].find_one({'app':'bbs'})
182         words = rec['bad_words']
183         out = rec['out_words']
184         rule = self.get_argument('aikotoba')
185         na = self.get_argument('name')
186         sub = self.get_argument('title')
187         com = self.get_argument('comment',None,False)
188         text = ''
189         i = 0
190         url = []
191         error = ''
192         if rule != u'げんき':
193             error = u'合言葉未入力.'
194         for word in out:
195             if word in com:
196                 error += u'禁止ワード.'
197                 break
198         for line in com.splitlines():
199             if error:
200                 break
201             for word in words:
202                 if word in line.lower():
203                     error += u'タグ違反.('+word+')'       
204             i += len(line)
205             obj = re.finditer('http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+', line)
206             for x in obj:
207                 if x.group() not in url:
208                     url.append(x.group())
209             j = 0
210             for x in line:
211                 if x == ' ':
212                     j += 1
213                 else:
214                     break
215             if j > 0: 
216                 line = line.replace(' ','&nbsp;',j)     
217             if len(line) == 0:
218                 text += '<br>'
219             else:
220                 text += '<p>'+self.link(line)+'</p>'
221         s = ''
222         for x in url:
223             s = s+'<tr><td><a href={0} class=livepreview target=_blank>{0}</a></td></tr>'.format(x)
224         if s != '':
225             text = text+'<table><tr><td>検出url:</td></tr>'+s+'</table>'
226         pw = self.get_argument('password')
227         if i > 1000:
228             error += u'文字数が1,000をこえました.'
229         if na == '':
230             na = u'誰かさん'
231         if sub == '':
232             sub = u'タイトルなし.'
233         article = self.application.db[dbname]
234         if article.count() == 0:
235             no = 1
236         else:            
237             items = article.find()
238             item = items.sort('number')[article.count()-1]
239             no = item['number']+1
240         if error == '':
241             self.set_cookie('aikotoba',escape.url_escape(rule))
242             s = datetime.now()
243             reg = {'number':no,'name':na,'title':sub,'comment':text,'raw':com,'password':pw,'date':s.strftime('%Y/%m/%d %H:%M')}
244             article.insert(reg)
245             self.set_cookie('username',escape.url_escape(na))
246             self.redirect('/'+dbname+'#article')
247         else:
248             self.render('regist.htm',content=error)
249     
250     def link(self,command):
251         i = 0
252         text = ''
253         obj = re.finditer('>>[0-9]+',command)
254         for x in obj:
255             s = '<a class=minpreview data-preview-url=/{0}?key={1} href=/{0}/userdel?job={1}>>>{1}</a>'.format(self.database,x.group()[2:])
256             text = text+command[i:x.start()]+s
257             i = x.end()
258         else:
259             text = text+command[i:]
260         return text
261     
262 class AdminHandler(BaseHandler):
263     @web.authenticated               
264     def get(self,dbname,page='0'):
265         if dbname == '':
266             dbname = self.get_argument('record','')
267         if self.application.collection(dbname) is False:
268             raise web.HTTPError(404)
269         table = self.application.db[dbname] 
270         rec = table.find().sort('number')                   
271         mente = self.application.db['params'].find_one({'app':'bbs'})
272         if mente['mentenance'] is True:
273             check = 'checked=checked'
274         else:
275             check = ''
276         pos = self.application.gpos(dbname,page)
277         i = mente['count']
278         start = (pos-1)*i
279         if start < 0:
280             start = table.count()-i
281             if start < 0:
282                 start = 0
283         rec.skip(start).limit(i)
284         self.render('modules/admin.htm',position=pos,records=rec,mente=check,password=mente['password'],db=dbname)
285
286 class AdminConfHandler(BaseHandler):
287     @web.authenticated
288     def post(self,dbname,func):
289         if func == 'set':
290             param = self.application.db['params'].find_one({'app':'bbs'})
291             if self.get_argument('mente','') == 'on':
292                 mente = True
293             else:
294                 mente = False  
295             word = self.get_argument('pass','')
296             if word == '':
297                 self.render('regist.htm',content='パスワードを設定してください')
298                 return
299             else:
300                 param['mentenance']=mente
301                 param['password']=word  
302                 self.application.db['params'].save(param)
303         elif func == 'del':
304             table = self.application.db[dbname]
305             for x in self.get_arguments('item'):
306                 table.remove({'number':int(x)})
307         self.redirect('/'+dbname+'/admin/0/')
308           
309 class UserHandler(web.RequestHandler):
310     def get(self,dbname):
311         q = self.get_query_argument('job','0',strip=True)
312         tb = self.application.db[dbname]
313         num = self.application.page(tb,int(q))
314         self.redirect('/{0}{1}#{2}'.format(dbname,num,q))
315         
316     def post(self,dbname):
317         number = self.get_argument('number')
318         if number.isdigit() is True:
319             num = int(number)
320             if 'password' in self.request.arguments.keys():
321                 pas = self.get_argument('password')
322             else:
323                 num = self.application.page(dbname,num)
324                 self.redirect('/{0}{1}#{2}'.format(dbname, num, number))
325                 return
326             table = self.application.db[dbname]
327             obj = table.find_one({'number':num})
328             if obj and(obj['password'] == pas):
329                 table.update({'number':num},{'$set':{'title':u'削除されました','name':'','comment':u'<i><b>投稿者により削除されました</b></i>','raw':''}})
330                 self.redirect('/'+dbname+self.application.page(table,num)+'#'+number)
331             else:
332                 self.redirect('/'+dbname)
333
334 class SearchHandler(web.RequestHandler):       
335     def post(self,dbname=''):
336         arg = self.get_argument('word1')
337         self.word = arg[:]
338         self.andor = self.get_argument('type')
339         self.radiobox = self.get_argument('filter')       
340         if dbname == '':
341             rec = []
342             for x in self.application.coll():
343                 moji = self.search(x)
344                 for y in sorted(moji,key=lambda k: k['number']):
345                     y['dbname'] = x
346                     rec.append(y) 
347         else:
348             rec = sorted(self.search(dbname),key=lambda x: x['number'])
349         self.render('modules/search.htm',records=rec,word1=arg,db=dbname)
350
351     def get(self,dbname=''):
352         if self.application.collection(dbname) is False and dbname != '':
353             raise web.HTTPError(404)
354         self.render('modules/search.htm',records=[],word1='',db=dbname)
355     
356     def search(self,dbname):
357         table = self.application.db[dbname]    
358         andor = self.andor == 'OR'
359         element = self.word.split()
360         elm = []
361         for x in element:
362             if x != '':
363                 elm.append(re.escape(x))
364         if self.radiobox == 'comment':
365             query = []
366             for qu in elm:
367                 query.append({'raw':re.compile(qu,re.IGNORECASE)})
368             if len(query) == 0:
369                 return
370             if andor:    
371                 result = table.find({'$or':query})
372                 color = 'yellow'
373             else:
374                 result = table.find({'$and':query})
375                 color = 'aqua'
376             for x in result:
377                 com = ''
378                 i = 0
379                 for text in x['raw'].splitlines():
380                     for y in text:
381                         if y == ' ':
382                             i += 1
383                         else:
384                             break
385                     text = text.replace(' ','&nbsp;',i)                  
386                     for y in element:                        
387                         if y.lower() in text.lower():
388                             com += '<p style=background-color:'+color+'>'+text+'<br></p>'  
389                             break                          
390                     else:
391                         if text == '':
392                             com += '<br>'
393                         else:
394                             com += '<p>'+text+'</p>'
395                 x['comment'] = com
396                 yield x       
397         else:
398             query = []
399             for x in element:
400                 if x != '':
401                     query.append({'name':x})
402             if len(query) == 0:
403                 return
404             for x in table.find({'$or':query}):
405                 yield x  
406                 
407 class HelpHandler(web.RequestHandler):
408     def get(self):
409         self.render('help.htm',req='')
410     
411     def post(self):
412         com = self.get_argument('help','')
413         line = com.splitlines(True)
414         com = ''
415         for x in line:
416             com += '<p>'+x
417         time = datetime.now()
418         db = self.application.db['master']
419         db.insert({'comment':com,'time':time.strftime('%Y/%m/%d')})
420         self.render('help.htm',req='送信しました')
421        
422 class MasterHandler(BaseHandler):
423     @web.authenticated  
424     def get(self):
425         if self.current_user == b'admin':
426             com = self.application.db['master'].find()
427             sum = self.application.db['temp'].find().count()
428             self.render('master.htm',com=com,sum=sum)
429         else:
430             raise web.HTTPError(404)
431     
432 class AlertHandler(web.RequestHandler):
433     def get(self):
434         db = self.get_query_argument('db')
435         num = self.get_query_argument('num')
436         table = self.application.db[db]
437         tb = table.find_one({'number':int(num)})
438         com = tb['comment']
439         time = datetime.now().strftime('%Y/%m/%d')
440         s = self.application.page(table,int(num))
441         link = '/'+db+s+'#'+num  
442         jump = '<p><a href={0}>{0}</a>'.format(link)
443         d = datetime.now().weekday()
444         table = self.application.db['temp']
445         table.remove({'date':{'$ne':d}})
446         result = table.insert(
447             {'comment':com+jump,'time':time,'link':link,'date':d,'db':db,'num':num})
448         self.render('alert.htm',com=com+jump,num=str(result))
449         
450     def post(self):
451         id = ObjectId(self.get_argument('num'))
452         table = self.application.db['temp']
453         tb = table.find_one({'_id':id})      
454         link = tb['link']
455         com = self.get_argument('com')
456         table.remove({'_id':id})
457         if self.get_argument('cancel','') == 'cancel':
458             self.redirect(link)
459             return
460         if com != '':
461             tb['comment'] = com+tb['comment']
462         del tb['date']
463         table = self.application.db['master']
464         table.insert(tb)
465         self.redirect(link)
466         
467 class CleanHandler(web.RequestHandler):
468     def post(self):
469         bool = self.get_argument('all', 'false').lower()
470         table = self.application.db['master']
471         if bool == 'true':
472             table.remove()
473             self.application.db['temp'].remove()
474         elif bool == 'false':
475             for x in list(table.find()):           
476                 if not 'num' in x.keys():
477                     table.remove({'_id':x['_id']})
478                 else:
479                     item = self.application.db[x['db']].find_one({'number':int(x['num'])})
480                     if (not item)or(item['raw'] == ''):
481                         table.remove({'_id':x['_id']})   
482         com = self.application.db['master'].find()
483         sum = self.application.db['temp'].find().count()
484         self.render('master.htm', com=com, sum=sum)
485                                         
486 class FooterModule(web.UIModule):
487     def render(self,number,url,link):
488         return self.render_string('modules/footer.htm',index=number,url=url,link=link)
489     
490 class HeadlineApi(web.RequestHandler):
491     def get(self):
492         response = {}
493         for coll in self.application.coll():
494             table = self.application.db[coll]
495             if table.count() == 0:
496                 mydict = {}
497             else:
498                 text = table.find().sort('number')[table.count()-1]
499                 mydict = {'number':text['number'],'name':text['name'],'title':text['title'],'comment':text['raw'][0:20]}
500             response[coll] = mydict                 
501         self.write(json.dumps(response,ensure_ascii=False))
502         
503 class ArticleApi(web.RequestHandler):
504     def get(self,dbname,number):
505         response = None
506         if self.application.collection(dbname) == True:
507             table = self.application.db[dbname]
508             response = table.find_one({'number':int(number)})      
509         if not response:
510            response = {}
511         else:
512             del response['_id']
513             del response['comment']
514             del response['password']
515         self.write(json.dumps(response,ensure_ascii=False))      
516             
517     def post(self,dbname,name,title,article):
518         coll = self.application.db[dbname] 
519         coll.insert({'name':name,'title':title,'comment':article})
520         
521 class ListApi(web.RequestHandler):
522     def get(self,dbname):
523         response = None
524         if self.application.collection(dbname) is True:
525             table = self.application.db[dbname]
526             response = {}
527             for data in table.find().sort('number'):
528                 response[data['number']] = data['raw'][0:20]
529         if not response:
530             response = {}
531         self.write(json.dumps(response,ensure_ascii=False))
532
533 class WebHookHandler(web.RequestHandler):        
534     def main(self, no):
535         #pz = pytz.timezone('Asia/Tokyo')
536         now = datetime.now()#pz)
537         t = now.hour
538         w = now.weekday()
539         if (w < 5)and(t >= 9)and(t < 16):
540             return u'仕事中.'
541         table, na = self.users()
542         item = table.find({'no':re.compile(no,re.IGNORECASE)})
543         if item.count() == 1:
544             x = item[0]
545             ans = x['name']+'\n'+x['no']
546         elif item.count() > 1:
547             ans = ''    
548             obj = list(item)
549             list1 = sorted(obj, key=lambda k:k['name'])
550             for x in list1:
551                 if x['name'] == list1[0]['name']:
552                     ans += x['name']+'\n'+x['no']+'\n'
553                 else:
554                     break
555             else:
556                 return ans       
557             ans = self.itr(sorted(list1, key=lambda k:k['no']))
558         else:
559             ans = self.itr(table.find().sort('no'))
560             ans = '-*-'+na+' list-*-\n'+ans
561         return ans
562     
563     def itr(self, item):
564         ans = ''
565         for x in item:
566             ans += '【'+x['no']+'】 '
567         return ans
568     
569     def help(self):
570         s = '-*-database names-*-\n'
571         out = ['objectlabs-system','objectlabs-system.admin.collections','users_bot']
572         for x in self.application.db.collection_names(include_system_collections=False):
573             if not x in out and x[-4:] == '_bot' and x != '_bot':
574                 s += x[:-4]+'\n'
575         return s
576     
577     def setting(self, dbname):
578         dbname = dbname.lower()+'_bot'
579         ca = self.application.db.collection_names(include_system_collections=False)
580         if 'users_bos' in ca:
581             ca.remove('users_bot')
582         if dbname in ca:
583             db = self.application.db['users_bot']
584             item = db.find_one({'name':self.uid})
585             if item['dbname'] == dbname:
586                 return False
587             else:
588                 db.update({'name':self.uid}, {'name':self.uid, 'dbname':dbname})
589                 return True
590         return False
591
592     def users(self):
593         db = self.application.db['users_bot']
594         item = db.find_one({'name':self.uid})
595         x = item['dbname']
596         return self.application.db[x], x[:-4]
597                           
598     def post(self):
599         '''
600         signature = self.request.headers['X-Line-Signature']
601         body = self.request.body
602         parser = WebhookParser(self.application.ch)
603         try:
604             parser.parse(body, signature)
605         except InvalidSignatureError:
606             web.HTTPError(404)
607             return
608         '''
609         dic = escape.json_decode(self.request.body)              
610         for event in dic['events']:
611             if 'replyToken' in event.keys():
612                 self.uid = event['source']['userId']
613                 bot = 'users_bot'               
614                 if event['type'] == 'unfollow':
615                     self.application.db[bot].remove({'name':self.uid})
616                     return
617                 elif event['type'] != 'message' or event['message']['type'] != 'text':
618                     return
619                 item = self.application.db['params'].find_one({'app':'bot'})
620                 if item:
621                     de = item['default']
622                 else:
623                     de = '_bot'         
624                 if item and 'access_token' in item.keys():
625                     token = item['access_token']
626                 else:      
627                     token =self.application.tk
628                 if not bot in self.application.db.collection_names() or not self.application.db[bot].find_one({'name':self.uid}):
629                     db = self.application.db[bot]
630                     db.insert({'name':self.uid, 'dbname':de})
631                 x = event['message']['text']     
632                 if self.setting(x):
633                     te = u'設定完了.'
634                 elif x == '?':
635                     te = self.help()
636                 else:
637                     te = self.main(x)
638                 linebot = LineBotApi(token)            
639                 linebot.reply_message(event['replyToken'], TextSendMessage(text=te))
640
641 class InitHandler(web.RequestHandler):
642     def get(self):        
643         de = self.get_argument('default', '')     
644         if de == '':
645             names = self.application.db.collection_names()
646             db = []
647             for x in names:
648                 if x[-4:] == '_bot' and x != 'users_bot':
649                     db.append(x[:-4])
650             self.render('init.htm',db=db)
651             return
652         tb = self.application.db['params']
653         if tb.find_one({'app':'bot'}):
654             tb.update({'app':'bot'}, {'app':'bot', 'default':de+'_bot'})
655         else:
656             tb.insert({'app':'bot', 'default':de+'_bot'})
657         for x in glob.glob('./*.txt'):
658             f = open(x)
659             data = f.read()
660             f.close()
661             self.main(x[2:-4].lower(), data)
662     
663     def main(self, name, data):
664         if name == 'requirements':
665             return
666         item = []
667         dic = None
668         for x in data.split('\n'):
669             if len(x) > 0 and x[0] == '@':
670                 dic = {}
671                 dic['name'] = x[1:]
672             elif dic:
673                 dic['no'] = x
674                 item.append(dic)
675         table = self.application.db[name+'_bot']
676         table.remove()
677         for x in item:
678             table.insert(x) 
679             
680 class TokenHandler(web.RequestHandler):
681     def on_response(self, response):
682         dic = escape.json_decode(response.body)
683         token = dic['access_token']
684         table = self.application.db['params']
685         data = {'app':'bot', 'access_token':token}
686         if table.find_one({'app':'bot'}):
687             table.save(data)
688         else:
689             table.insert(data)
690         self.finish()
691
692     #@web.asynchronous
693     def get(self):
694         url = 'https://api.line.me/v2/oauth/accessToken'
695         headers = 'application/x-www-form-urlencoded'
696         data = {'grant_type':'client_credentials', 'client_id':self.application.id, 'client_secret':self.application.ch}
697         body = urllib.parse.urlencode(data)
698         req = httpclient.HTTPRequest(url=url,method='POST',headers=headers,body=body)
699         http = httpclient.AsyncHTTPClient()
700         http.fetch(req, callback=self.on_response)
701
702 class Application(web.Application):
703     id = os.environ['Bot_Id']  
704     ch = os.environ['Channel_Secret']
705     uri = os.environ['MONGODB_URI']
706     ac = os.environ['ACCOUNT']   
707     tk = os.environ['long_token']
708     db = pymongo.MongoClient(uri)[ac]
709     def __init__(self):
710         handlers = [(r'/',NaviHandler),(r'/login',LoginHandler),(r'/logout',LogoutHandler),(r'/title',TitleHandler),
711                     (r'/headline/api',HeadlineApi),(r'/read/api/([a-zA-Z0-9_]+)/([0-9]+)',ArticleApi),
712                     (r'/write/api/([a-zA-Z0-9_%]+)/()/()/()',ArticleApi),(r'/list/api/([a-zA-Z0-9_%]+)',ListApi),
713                     (r'/help',HelpHandler),(r'/master',MasterHandler),(r'/alert',AlertHandler),(r'/jump',JumpHandler),
714                     (r'/callback',WebHookHandler),(r'/init',InitHandler),(r'/search',SearchHandler),(r'/clean',CleanHandler),(r'/token',TokenHandler),
715                     (r'/([a-zA-Z0-9_%]+)',IndexHandler),(r'/([a-zA-Z0-9_%]+)/([0-9]+)/',IndexHandler),
716                     (r'/([a-zA-Z0-9_%]+)/admin/([0-9]+)/*',AdminHandler),(r'/([a-zA-Z0-9_%]+)/admin/([a-z]+)/*',AdminConfHandler),(r'/([a-zA-Z0-9_%]+)/userdel',UserHandler),
717                     (r'/([a-zA-Z0-9_%]+)/search',SearchHandler),(r'/([a-zA-Z0-9_%]+)/regist',RegistHandler)]
718         settings = {'template_path':os.path.join(os.path.dirname(__file__),'templates'),
719                         'static_path':os.path.join(os.path.dirname(__file__),'static'),
720                         'ui_modules':{'Footer':FooterModule},
721                         'cookie_secret':os.environ['cookie'],
722                         'xsrf_cookies':False,
723                         #'debug':True,
724                         'login_url':'/login'
725                         }
726         super().__init__(handlers,**settings)
727  
728     def gpos(self,dbname,page):
729         params = self.db['params'].find_one({'app':'bbs'})
730         pos = int(page)
731         if pos <= 0:
732             pos = 0
733         elif (pos-1)*params['count'] >= self.db[dbname].count():
734             pos = 0
735         return pos
736
737     def page(self,table,number):
738         rec = table.find({'number':{'$lte':number}}).count()
739         s = self.db['params'].find_one({'app':'bbs'})
740         conf = int(s['count'])
741         if table.find().count() - rec >= conf:
742             return '/'+str(1+rec//conf)+'/'
743         else:
744             return ''
745     
746     def collection(self,name):
747         if name in self.db.collection_names():
748             return True
749         else:
750             return False
751
752     def coll(self):
753         name = self.db.collection_names()
754         for x in ['objectlabs-system.admin.collections','objectlabs-system','system.indexes',
755                   'params','master','temp']:
756             if x in name:
757                 name.remove(x)
758         for x in name:
759             if x[-4:] == '_bot':
760                 name.remove(x)
761         return sorted(name)
762    
763 if __name__ == '__main__':
764     app = Application()
765     http_server = httpserver.HTTPServer(app)
766     port = int(os.environ.get('PORT',5000))
767     http_server.listen(port)
768     ioloop.IOLoop.instance().start()