OSDN Git Service

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