├── README.md └── ezoe.go /README.md: -------------------------------------------------------------------------------- 1 | # ezoe 2 | 3 | 質問ではない 4 | 5 | ## Usage 6 | 7 | listing questions/answers 8 | ``` 9 | $ ezoe 10 | ``` 11 | 12 | post question 13 | ``` 14 | $ ezoe 今日の朝ごはんは何ですか? 15 | ``` 16 | 17 | ## Requirements 18 | 19 | golang 20 | 21 | ## Installation 22 | 23 | ``` 24 | go get github.com/mattn/ezoe 25 | ``` 26 | 27 | ## License 28 | 29 | MIT 30 | 31 | ## Author 32 | 33 | Yasuhiro Matsumoto (a.k.a mattn) 34 | -------------------------------------------------------------------------------- /ezoe.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "errors" 6 | "flag" 7 | "fmt" 8 | "io/ioutil" 9 | "net/http" 10 | "net/http/cookiejar" 11 | "net/url" 12 | "os" 13 | "os/user" 14 | "path/filepath" 15 | "regexp" 16 | "strings" 17 | "time" 18 | 19 | "github.com/PuerkitoBio/goquery" 20 | "github.com/bgentry/speakeasy" 21 | "github.com/mattn/go-colorable" 22 | "github.com/mattn/go-pkg-rss" 23 | ) 24 | 25 | const ( 26 | Red = "\x1b[31;1m" 27 | Green = "\x1b[32;1m" 28 | White = "\x1b[37;1m" 29 | End = "\x1b[0m" 30 | ) 31 | 32 | var ( 33 | you = flag.String("u", "EzoeRyou", "who do you want to ask to?") 34 | stdout = colorable.NewColorableStdout() 35 | ) 36 | 37 | var highlights = []*regexp.Regexp{ 38 | regexp.MustCompile("質問ではない。?"), 39 | regexp.MustCompile("不?自由"), 40 | } 41 | 42 | func doLogin() error { 43 | me, err := user.Current() 44 | if err != nil { 45 | return err 46 | } 47 | 48 | uri, err := url.Parse("http://ask.fm/") 49 | if err != nil { 50 | return err 51 | } 52 | 53 | session := filepath.Join(me.HomeDir, ".ezoe.session") 54 | if b, err := ioutil.ReadFile(session); err == nil { 55 | http.DefaultClient.Jar.SetCookies(uri, []*http.Cookie{&http.Cookie{ 56 | Name: "_ask.fm_session", 57 | Value: strings.TrimSpace(string(b)), 58 | }}) 59 | } 60 | 61 | doc, err := goquery.NewDocument("http://ask.fm/login") 62 | if err != nil { 63 | return err 64 | } 65 | 66 | if doc.Find(".link-logout").Length() > 0 { 67 | return nil 68 | } 69 | 70 | token := "" 71 | doc.Find("input[name=authenticity_token]").Each(func(_ int, s *goquery.Selection) { 72 | if attr, ok := s.Attr("value"); ok { 73 | token = attr 74 | } 75 | }) 76 | 77 | scanner := bufio.NewScanner(os.Stdin) 78 | for { 79 | fmt.Print("User: ") 80 | if scanner.Scan() { 81 | break 82 | } 83 | } 84 | user := scanner.Text() 85 | password, err := speakeasy.Ask("Password: ") 86 | if err != nil { 87 | return err 88 | } 89 | 90 | params := url.Values{} 91 | params.Add("authenticity_token", token) 92 | params.Add("login", user) 93 | params.Add("password", password) 94 | 95 | req, err := http.NewRequest("POST", "http://ask.fm/session", strings.NewReader(params.Encode())) 96 | if err != nil { 97 | return err 98 | } 99 | req.Header.Add("Referer", "http://ask.fm/login") 100 | resp, err := http.DefaultClient.Do(req) 101 | if err != nil { 102 | return err 103 | } 104 | resp.Body.Close() 105 | if resp.StatusCode != 200 { 106 | return errors.New(resp.Status) 107 | } 108 | 109 | doc, err = goquery.NewDocument("http://ask.fm/account/wall") 110 | if err != nil { 111 | return err 112 | } 113 | 114 | if doc.Find(".link-logout").Length() == 0 { 115 | return errors.New("正しいユーザもしくはパスワードではない") 116 | } 117 | 118 | for _, cookie := range resp.Cookies() { 119 | if cookie.Name == "_ask.fm_session" { 120 | return ioutil.WriteFile(session, []byte(cookie.Value), 0600) 121 | } 122 | } 123 | return nil 124 | } 125 | 126 | func doPost(question string) error { 127 | doc, err := goquery.NewDocument("http://ask.fm/" + *you) 128 | if err != nil { 129 | return err 130 | } 131 | 132 | token := "" 133 | doc.Find("input[name=authenticity_token]").Each(func(_ int, s *goquery.Selection) { 134 | if attr, ok := s.Attr("value"); ok { 135 | token = attr 136 | } 137 | }) 138 | 139 | params := url.Values{} 140 | params.Add("authenticity_token", token) 141 | params.Add("question[question_text]", question) 142 | params.Add("question[force_anonymous]", "force_anonymous") 143 | 144 | req, err := http.NewRequest("POST", "http://ask.fm/"+*you+"/questions/create", strings.NewReader(params.Encode())) 145 | if err != nil { 146 | return err 147 | } 148 | req.Header.Add("Referer", "http://ask.fm/"+*you) 149 | resp, err := http.DefaultClient.Do(req) 150 | if err != nil { 151 | return err 152 | } 153 | defer resp.Body.Close() 154 | if resp.StatusCode != 200 { 155 | return errors.New(resp.Status) 156 | } 157 | return nil 158 | } 159 | 160 | func doList() error { 161 | return feeder.New(20, true, nil, 162 | func(feed *feeder.Feed, ch *feeder.Channel, fi []*feeder.Item) { 163 | sort_items: 164 | for n := 0; n < len(fi)-1; n++ { 165 | tn, err := time.Parse(time.RFC1123Z, fi[n].PubDate) 166 | if err != nil { 167 | break sort_items 168 | } 169 | for m := len(fi) - 1; m > n; m-- { 170 | tm, err := time.Parse(time.RFC1123Z, fi[n].PubDate) 171 | if err != nil { 172 | break sort_items 173 | } 174 | if tn.After(tm) { 175 | fi[n], fi[m] = fi[m], fi[n] 176 | } 177 | } 178 | } 179 | for _, item := range fi { 180 | if len(item.Links) == 0 { 181 | continue 182 | } 183 | // URL 184 | fmt.Println(item.Links[0].Href) 185 | // Title 186 | fmt.Fprintln(stdout, " "+Red+item.Title+End) 187 | // Description 188 | desc := item.Description 189 | for _, h := range highlights { 190 | desc = h.ReplaceAllString(desc, Green+"$0"+White) 191 | } 192 | fmt.Fprintln(stdout, " "+White+desc+End) 193 | fmt.Println("") 194 | } 195 | }, 196 | ).Fetch("http://ask.fm/feed/profile/"+*you+".rss", nil) 197 | } 198 | 199 | func doEzoe() error { 200 | flag.Parse() 201 | 202 | jar, err := cookiejar.New(nil) 203 | if err != nil { 204 | return err 205 | } 206 | http.DefaultClient.Jar = jar 207 | 208 | if flag.NArg() > 0 { 209 | if err = doLogin(); err != nil { 210 | return err 211 | } 212 | return doPost(strings.Join(flag.Args(), " ")) 213 | } 214 | return doList() 215 | } 216 | 217 | func main() { 218 | if err := doEzoe(); err != nil { 219 | fmt.Fprintln(os.Stderr, os.Args[0]+":", err) 220 | os.Exit(1) 221 | } 222 | } 223 | --------------------------------------------------------------------------------