| 1 | import sys |
|---|
| 2 | import clr |
|---|
| 3 | import re |
|---|
| 4 | import thread |
|---|
| 5 | import time |
|---|
| 6 | |
|---|
| 7 | import Misuzilla.Applications.TwitterIrcGateway |
|---|
| 8 | import Misuzilla.Applications.TwitterIrcGateway.AddIns |
|---|
| 9 | import Misuzilla.Applications.TwitterIrcGateway.AddIns.Console |
|---|
| 10 | |
|---|
| 11 | from System import * |
|---|
| 12 | from System.Threading import Thread, ThreadStart |
|---|
| 13 | from System.Collections.Generic import * |
|---|
| 14 | from System.Diagnostics import Trace |
|---|
| 15 | from Misuzilla.Applications.TwitterIrcGateway import Status, Statuses, User, Users, Utility |
|---|
| 16 | from Misuzilla.Applications.TwitterIrcGateway.AddIns import IConfiguration, ConfigurationPropertyInfo |
|---|
| 17 | from Misuzilla.Applications.TwitterIrcGateway.AddIns.Console import ConsoleAddIn, Console, Context |
|---|
| 18 | from Misuzilla.Applications.TwitterIrcGateway.AddIns.DLRIntegration import DLRIntegrationAddIn, DLRBasicConfiguration, DLRContextHelper |
|---|
| 19 | |
|---|
| 20 | class ScrapingContext(Context): |
|---|
| 21 | def Initialize(self): |
|---|
| 22 | self.scraping = Scraping.instance() |
|---|
| 23 | self.config = self.scraping.config |
|---|
| 24 | pass |
|---|
| 25 | |
|---|
| 26 | def GetCommands(self): |
|---|
| 27 | dict = Context.GetCommands(self) |
|---|
| 28 | dict["Interval"] = "取得間隔を設定します。" |
|---|
| 29 | dict["Relogin"] = "再ログインします。" |
|---|
| 30 | dict["Enable"] = "スクレイピングを有効にします。" |
|---|
| 31 | dict["Disable"] = "スクレイピングを無効にします。" |
|---|
| 32 | return dict |
|---|
| 33 | |
|---|
| 34 | def OnUninitialize(self): |
|---|
| 35 | pass |
|---|
| 36 | |
|---|
| 37 | def get_Configurations(self): |
|---|
| 38 | return Array[IConfiguration]([ self.config ]) |
|---|
| 39 | |
|---|
| 40 | # Implementation |
|---|
| 41 | def Interval(self, args): |
|---|
| 42 | if String.IsNullOrEmpty(args): |
|---|
| 43 | self.Console.NotifyMessage("取得間隔を指定してください。") |
|---|
| 44 | return |
|---|
| 45 | interval = int(args, 10) |
|---|
| 46 | self.scraping.interval = interval |
|---|
| 47 | self.config.SetValue("Interval", interval) |
|---|
| 48 | self.Console.NotifyMessage("取得間隔を %s 秒に設定しました。" % args) |
|---|
| 49 | self.scraping.start() |
|---|
| 50 | |
|---|
| 51 | def Relogin(self, args): |
|---|
| 52 | self.Console.NotifyMessage("ログインしています...") |
|---|
| 53 | CurrentSession.TwitterService.CookieLogin() |
|---|
| 54 | self.Console.NotifyMessage("ログインしました。") |
|---|
| 55 | |
|---|
| 56 | def Enable(self, args): |
|---|
| 57 | self.config.SetValue("Enable", True) |
|---|
| 58 | self.scraping.enable = True |
|---|
| 59 | self.scraping.start() |
|---|
| 60 | self.Console.NotifyMessage("スクレイピングを有効にしました") |
|---|
| 61 | |
|---|
| 62 | def Disable(self, args): |
|---|
| 63 | self.config.SetValue("Enable", False) |
|---|
| 64 | self.scraping.enable = False |
|---|
| 65 | self.Console.NotifyMessage("スクレイピングを無効にしました") |
|---|
| 66 | |
|---|
| 67 | class Scraping(Object): |
|---|
| 68 | @classmethod |
|---|
| 69 | def instance(klass): |
|---|
| 70 | if not hasattr(klass, 'instance_'): |
|---|
| 71 | klass.instance_ = Scraping() |
|---|
| 72 | return klass.instance_ |
|---|
| 73 | |
|---|
| 74 | def __init__(self): |
|---|
| 75 | # 普通の #Console にコンテキストを追加する |
|---|
| 76 | CurrentSession.AddInManager.GetAddIn[ConsoleAddIn]().RegisterContext(DLRContextHelper.Wrap(CurrentSession, "ScrapingContext", ScrapingContext), "Scraping", "スクレイピングの設定を行うコンテキストに切り替えます") |
|---|
| 77 | CurrentSession.AddInManager.GetAddIn[DLRIntegrationAddIn]().BeforeUnload += self.onBeforeUnload |
|---|
| 78 | CurrentSession.PostProcessTimelineStatuses += self.onPostProcessTimelineStatuses |
|---|
| 79 | self.running = False |
|---|
| 80 | self.thread = None |
|---|
| 81 | |
|---|
| 82 | self.config = DLRBasicConfiguration(CurrentSession, "ScrapingContext", Array[ConfigurationPropertyInfo]([ConfigurationPropertyInfo("Enable", "スクレイピングを利用するかどうか", Boolean, False, None), ConfigurationPropertyInfo("Interval", "取得間隔", Int32, 30, None), ConfigurationPropertyInfo("DisableTimelineApi", "APIによるタイムライン取得を停止するかどうか", Boolean, False, None)])) |
|---|
| 83 | |
|---|
| 84 | self.interval = self.config.GetValue("Interval") |
|---|
| 85 | self.enable = self.config.GetValue("Enable") |
|---|
| 86 | |
|---|
| 87 | self.re_source = re.compile(r"<span>from (.*?)</span>") |
|---|
| 88 | self.re_statuses = re.compile(r"<li class=\"hentry u-.*? status.*?</li>", re.S) |
|---|
| 89 | self.re_content = re.compile(r"class=\"entry-content\">(.*?)</span>") |
|---|
| 90 | self.re_user = re.compile(r"class=\"screen-name\" title=\"([^\"]+)\">(.*?)</a>") |
|---|
| 91 | self.re_anchor = re.compile(r"<a href=\"(http://[^\"]*)\"[^>]*>.*?</a>") |
|---|
| 92 | self.re_tag = re.compile(r"<[^>]*>") |
|---|
| 93 | self.re_status_id = re.compile(r"id=\"status_(\d+)\"") |
|---|
| 94 | |
|---|
| 95 | def start(self): |
|---|
| 96 | if not self.running: |
|---|
| 97 | try: |
|---|
| 98 | CurrentSession.TwitterService.CookieLogin() |
|---|
| 99 | except: |
|---|
| 100 | pass |
|---|
| 101 | self.thread = Thread(ThreadStart(self.runProc)) |
|---|
| 102 | self.thread.Start() |
|---|
| 103 | |
|---|
| 104 | def runProc(self): |
|---|
| 105 | self.running = True |
|---|
| 106 | while self.interval > 0 and self.enable: |
|---|
| 107 | try: |
|---|
| 108 | self.fetchHome() |
|---|
| 109 | except: |
|---|
| 110 | Trace.WriteLine(sys.exc_info().ToString()) |
|---|
| 111 | Thread.Sleep(self.interval * 1000) |
|---|
| 112 | self.running = False |
|---|
| 113 | |
|---|
| 114 | def fetchHome(self): |
|---|
| 115 | home = CurrentSession.TwitterService.GETWithCookie("/home") |
|---|
| 116 | statuses = self.re_statuses.findall(home) |
|---|
| 117 | statuses.reverse() |
|---|
| 118 | for status in statuses: |
|---|
| 119 | s = Status() |
|---|
| 120 | # User |
|---|
| 121 | match = self.re_user.search(status) |
|---|
| 122 | s.User = User() |
|---|
| 123 | s.User.Id = 0 |
|---|
| 124 | s.User.Name = match.group(1) |
|---|
| 125 | s.User.ScreenName = match.group(2) |
|---|
| 126 | |
|---|
| 127 | # Status |
|---|
| 128 | s.Source = self.re_source.search(status).group(1) |
|---|
| 129 | s.Text = Utility.UnescapeCharReference(self.re_tag.sub(r"", self.re_anchor.sub(r"\1", self.re_content.search(status).group(1)))) |
|---|
| 130 | s.Id = int(self.re_status_id.search(status).group(1), 10) |
|---|
| 131 | s.CreatedAt = DateTime.Now |
|---|
| 132 | |
|---|
| 133 | #Trace.WriteLine(s.ToString()) |
|---|
| 134 | CurrentSession.TwitterService.ProcessStatus(s, Action[Status](lambda s1: CurrentSession.ProcessTimelineStatus(s1, False, False))) |
|---|
| 135 | |
|---|
| 136 | def onPostProcessTimelineStatuses(self, sender, e): |
|---|
| 137 | if e.IsFirstTime and self.requireDisableApi(): |
|---|
| 138 | CurrentSession.TwitterService.Interval = 360000 |
|---|
| 139 | CurrentSession.TwitterService.Stop() |
|---|
| 140 | CurrentSession.TwitterService.Start() |
|---|
| 141 | |
|---|
| 142 | def onBeforeUnload(self, sender, e): |
|---|
| 143 | CurrentSession.AddInManager.GetAddIn[ConsoleAddIn]().UnregisterContext(DLRContextHelper.Wrap(CurrentSession, "ScrapingContext", ScrapingContext)) |
|---|
| 144 | CurrentSession.PostProcessTimelineStatuses -= self.onPostProcessTimelineStatuses |
|---|
| 145 | self.interval = 0 |
|---|
| 146 | self.thread.Abort() |
|---|
| 147 | self.thread.Join(5000) |
|---|
| 148 | |
|---|
| 149 | def requireDisableApi(self): |
|---|
| 150 | return self.config.GetValue("DisableTimelineApi") |
|---|
| 151 | |
|---|
| 152 | scraping = Scraping.instance() |
|---|
| 153 | scraping.start() |
|---|