├── .gitignore ├── README.md ├── example └── example.go ├── src ├── example │ └── config.toml └── webqq │ └── searchqq.go ├── test ├── cool.png ├── l │ ├── l │ └── l.go ├── test └── test.go ├── webqq.sublime-project └── webqq ├── client.go ├── getinfo.go ├── getlist.go ├── log.go ├── login.go ├── pollmsg.go ├── sendbuddymsg.go ├── sendgroupmsg.go └── tools ├── simplejson ├── .travis.yml └── simplejson.go └── tools.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Compiled Object files, Static and Dynamic libs (Shared Objects) 2 | *.o 3 | *.a 4 | *.so 5 | 6 | # Folders 7 | _obj 8 | _test 9 | 10 | # Architecture specific extensions/prefixes 11 | *.[568vq] 12 | [568vq].out 13 | 14 | *.cgo1.go 15 | *.cgo2.c 16 | _cgo_defun.c 17 | _cgo_gotypes.go 18 | _cgo_export.* 19 | 20 | _testmain.go 21 | 22 | *.exe 23 | *.sublime-workspace 24 | /src/test 25 | .directory 26 | *.log 27 | *.log.*.00* 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | gowebqq (deprecated) 2 | ========= 3 | 4 | **There's no longer any interest from the author in QQ protocol.** 5 | 6 | webqq API for Golang 7 | 8 | Simple wrapper of webqq protocol. 9 | -------------------------------------------------------------------------------- /example/example.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/BurntSushi/toml" 6 | "io" 7 | "log" 8 | "os" 9 | "os/exec" 10 | "strings" 11 | "time" 12 | "webqq" 13 | ) 14 | 15 | type cfg struct { 16 | Username string 17 | Password string 18 | DailTimeout int 19 | VfCodeFile string 20 | VfCodeTrigger string 21 | LogChannelLen int64 22 | KickRelogin bool 23 | FileLog struct { 24 | Use bool 25 | Filename string 26 | Level int 27 | } 28 | ConsoleLog struct { 29 | Use bool 30 | Level int 31 | } 32 | } 33 | 34 | func main() { 35 | defer time.Sleep(1 * time.Second) 36 | config := new(cfg) 37 | _, err := toml.DecodeFile(`config.toml`, config) 38 | 39 | if err != nil { 40 | log.Fatalln("read config error:", err) 41 | } 42 | 43 | client := webqq.New(config.Username, config.Password, config.DailTimeout, config.LogChannelLen) //qq Number and Password,obtain a client object 44 | if config.FileLog.Use { 45 | client.SetLogger("file", fmt.Sprintf("{\"level\":%d,\"filename\":\"%s\"}", config.FileLog.Level, config.FileLog.Filename)) 46 | } 47 | if config.ConsoleLog.Use { 48 | client.SetLogger("console", fmt.Sprintf("{\"level\":%d}", config.ConsoleLog.Level)) 49 | } 50 | 51 | L: 52 | ret, err := client.LoginStep1() //test login to know if a validation image is necessary 53 | if err != nil { 54 | client.Error("login step 1 fail: error: %v", err) 55 | client.Info("relogin after 1 minute") 56 | time.Sleep(1 * time.Minute) 57 | goto L 58 | } 59 | 60 | if ret.NeedPic() { 61 | re, err := client.Get(ret.PicAddr) //get pic addr 62 | if err != nil { 63 | client.Error("get login pic fail: error: %v", err) 64 | } 65 | 66 | f, err := os.Create(config.VfCodeFile) //write image to file 67 | if err != nil { 68 | client.Critical("create vfcode pic fail: error: %v", err) 69 | return 70 | } 71 | io.Copy(f, re.Body) 72 | f.Close() 73 | 74 | re.Body.Close() 75 | 76 | trigger := strings.Fields(config.VfCodeTrigger) 77 | 78 | cmd := exec.Command(trigger[0], trigger[1:]...) 79 | out, _ := cmd.StdoutPipe() 80 | cmd.Stdin = os.Stdin 81 | 82 | s := `` 83 | go func() { fmt.Fscan(out, &s) }() 84 | 85 | err = cmd.Run() 86 | if err != nil { 87 | client.Critical("cannot run vf trigger: error: %v", err) 88 | return 89 | } 90 | 91 | ret.SetVFCode(s) //set validation code 92 | } 93 | 94 | //if ret.NeedPic()==false ret.SetVFCode() is unnecessary 95 | 96 | err = client.LoginStep2(ret) //true login 97 | if err != nil { 98 | client.Error("login step2 fail: error: %v", err) 99 | client.Info("wait 10 second to relogin") 100 | time.Sleep(10 * time.Second) 101 | client.Warn(`relogin`) 102 | goto L 103 | } 104 | 105 | for { 106 | msgraw, err := client.RawPoll() //qq use poll to get message. 107 | if err != nil { 108 | client.Error("poll message fail: error: %v", err) 109 | client.Warn("relogin") 110 | goto L 111 | } 112 | msg, err := webqq.ParseRawPoll(msgraw) 113 | if err != nil { 114 | client.Error("parse raw poll fail: error: %v", err) 115 | continue 116 | } 117 | 118 | for _, v := range msg { 119 | if v.IsNothing() { 120 | continue 121 | } 122 | 123 | if m, ok := v.IsBuddyMessage(); ok { 124 | 125 | err := client.SendBuddyMsgEasy(m.FromUin, m.MessageId, "你好"+m.Content[0]) 126 | if err != nil { 127 | fmt.Println(err) 128 | } 129 | gid, err := client.GetId(m.FromUin) 130 | client.Info("收到个人消息: %v ,id: %v", m.Content, gid) 131 | continue 132 | } 133 | if _, ok := v.IsBuddyStatusChange(); ok { 134 | continue 135 | } 136 | if m, ok := v.IsGroupMessage(); ok { 137 | err = client.SendGroupMsgEasy(m.FromUin, 55440000, "机器人重复说:"+m.Content[0]) 138 | if err != nil { 139 | client.Error("reply group message fail: error: %v", err) 140 | } 141 | 142 | gid, err := client.GetId(m.GroupCode) 143 | if err != nil { 144 | client.Warn("get gid fail %v", err) 145 | } 146 | id, err := client.GetId(m.FromUin) 147 | if err != nil { 148 | client.Warn("get id fail %v", err) 149 | } 150 | client.Info("收到群消息: %v, 群号: %v 来自号码: %v", m.Content, gid, id) 151 | continue 152 | } 153 | if ptwebqq, ok := v.IsNewPtwebqq(); ok { 154 | client.SetPtWebqq(ptwebqq) 155 | continue 156 | } 157 | if ok := v.IsKick(); ok { 158 | client.Warn("你被踢下线了") 159 | if config.KickRelogin == true { 160 | goto L 161 | } 162 | return 163 | } 164 | if _, ok := v.IsSysGMessage(); ok { 165 | continue 166 | } 167 | if _, ok := v.IsTips(); ok { 168 | continue 169 | } 170 | if _, ok := v.IsGroupWebMessage(); ok { 171 | continue 172 | } 173 | if _, ok := v.IsSystemMessage(); ok { 174 | continue 175 | } 176 | if _, ok := v.IsBuddylistChange(); ok { 177 | continue 178 | } 179 | client.Warn("unexpected message: %v", v) 180 | } 181 | 182 | } 183 | 184 | } 185 | -------------------------------------------------------------------------------- /src/example/config.toml: -------------------------------------------------------------------------------- 1 | Username = "123213" # 帐号 2 | Password = "123" # 密码 3 | DailTimeout = 2000000 # http超时时间 4 | VfCodeFile = "vfcode.png" # 如果有验证码的话,验证码保存文件名 5 | 6 | VfCodeTrigger = "recognize vfcode.png" # 收到验证码以后触发的bash命令。这里是验证码识别程序(如果你有的话),会把命令的stdout重定向到程序。如果没有识别程序,你也可以简单的用cat来代,然后人工识别 7 | 8 | LogChannelLen = 2000 # 日志模块缓冲大小 9 | KickRelogin = true # 如果true,在贝踢下线以会自动登录 10 | 11 | [FileLog] 12 | use = true 13 | filename = "log.log" 14 | level = 2 # 日志等级 0-5 15 | [ConsoleLog] 16 | use = true 17 | level = 0 18 | -------------------------------------------------------------------------------- /src/webqq/searchqq.go: -------------------------------------------------------------------------------- 1 | package webqq 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "io" 7 | "os" 8 | . "webqq/tools" 9 | ) 10 | 11 | func (this *Client) SearchQQ(id string) (bdinfo *BuddyInfo, err error) { 12 | re, err := this.Get(`http://captcha.qq.com/getimage?aid=1003901&0.3768496420234442`) 13 | 14 | f, _ := os.OpenFile("pp.png", os.O_TRUNC|os.O_RDWR|os.O_CREATE, 0644) 15 | io.Copy(f, re.Body) 16 | f.Close() 17 | s := `` 18 | fmt.Scan(&s) 19 | 20 | url := fmt.Sprintf( 21 | "http://s.web2.qq.com/api/search_qq_by_uin2?tuin=%s&verifysession=%s&code=%s&vfwebqq=%s&t=%s", 22 | id, 23 | this.verifysession, 24 | s, 25 | this.vfwebqq, 26 | this.timeStamp()) 27 | fmt.Println(url) 28 | re, err = this.Get(url) 29 | 30 | ErrHandle(err, `n`) 31 | byt := ReadByte(re.Body) 32 | fmt.Println(string(byt)) 33 | bdinfo = new(BuddyInfo) 34 | err = json.Unmarshal(byt, &cc{0, bdinfo}) 35 | return bdinfo, err 36 | } 37 | -------------------------------------------------------------------------------- /test/cool.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dovahcrow/gowebQQ/c299625b8f9fc5169095c65b99210b55954dac0d/test/cool.png -------------------------------------------------------------------------------- /test/l/l: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dovahcrow/gowebQQ/c299625b8f9fc5169095c65b99210b55954dac0d/test/l/l -------------------------------------------------------------------------------- /test/l/l.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "log" 7 | "os" 8 | "regexp" 9 | ) 10 | 11 | func main() { 12 | 13 | f, err := os.Open("../log.log.2014-07-18.001") 14 | if err != nil { 15 | log.Fatalln(err) 16 | } 17 | b, _ := ioutil.ReadAll(f) 18 | s := string(b) 19 | reg := regexp.MustCompile(`(?m)parse raw poll: msg is {"retcode":0,"result":\[{"poll_type":"\w+"`) 20 | sub := reg.FindAllStringSubmatch(s, -1) 21 | for _, v := range sub { 22 | fmt.Println(v) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /test/test: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dovahcrow/gowebQQ/c299625b8f9fc5169095c65b99210b55954dac0d/test/test -------------------------------------------------------------------------------- /test/test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "log" 7 | "os" 8 | "os/exec" 9 | "time" 10 | "webqq" 11 | ) 12 | 13 | func main() { 14 | 15 | cl := webqq.New(`173165159`, `R9:T4K6@`, 1000, 20000000) //qq Number and Password,obtain a client object 16 | L: 17 | ret, err := cl.LoginStep1() //test login to know if a validation image is necessary 18 | if err != nil { 19 | log.Fatalln(err) 20 | 21 | } 22 | if ret.NeedPic() { 23 | re, err := cl.Get(ret.PicAddr) //get pic addr 24 | if err != nil { 25 | log.Fatalln(err) 26 | 27 | } 28 | 29 | f, err := os.Create(`cool.png`) //write image to file 30 | if err != nil { 31 | log.Fatalln(err) 32 | 33 | } 34 | io.Copy(f, re.Body) 35 | f.Close() 36 | 37 | re.Body.Close() 38 | 39 | exec.Command(`gwenview`, `cool.png`).Run() //show the image 40 | var s string 41 | fmt.Scanf("%s", &s) //recognize it 42 | ret.SetVFCode(s) //set validation code 43 | } 44 | 45 | //if ret.NeedPic()==false ret.SetVFCode() is unnecessary 46 | 47 | err = cl.LoginStep2(ret) //true login 48 | if err != nil { 49 | time.Sleep(10 * time.Second) 50 | goto L 51 | 52 | } 53 | 54 | for { 55 | msgraw, err := cl.RawPoll() //qq use poll to get message. 56 | if err != nil { 57 | log.Fatalln(err) 58 | } 59 | 60 | msg, err := webqq.ParseRawPoll(msgraw) 61 | if err != nil { 62 | log.Fatalln(err) 63 | } 64 | for _, v := range msg { 65 | if v.IsNothing() { 66 | continue 67 | } 68 | 69 | if _, ok := v.IsBuddyMessage(); ok { 70 | // fmt.Printf("%+v\n", bmsg) 71 | // err := cl.SendBuddyMsgEasy(bmsg.FromUin, bmsg.MessageId+1, "got"+bmsg.Content[0]) 72 | // if err != nil { 73 | // fmt.Println(err) 74 | // } 75 | continue 76 | } 77 | if _, ok := v.IsBuddyStatusChange(); ok { 78 | continue 79 | } 80 | if _, ok := v.IsGroupMessage(); ok { 81 | // err := cl.SendGroupMsgEasy(gmsg.FromUin, gmsg.MessageId, "fa") 82 | // if err != nil { 83 | // fmt.Println(err) 84 | // } 85 | // fmt.Printf("%+v\n", gmsg) 86 | continue 87 | } 88 | if ptwebqq, ok := v.IsNewPtwebqq(); ok { 89 | cl.SetPtWebqq(ptwebqq) 90 | continue 91 | } 92 | if ok := v.IsKick(); ok { 93 | return 94 | } 95 | if s, ok := v.IsSysGMessage(); ok { 96 | fmt.Printf("%+v\n", s) 97 | continue 98 | } 99 | if tips, ok := v.IsTips(); ok { 100 | fmt.Println("tips", tips) 101 | continue 102 | } 103 | if gwm, ok := v.IsGroupWebMessage(); ok { 104 | fmt.Println("gwm", gwm) 105 | continue 106 | } 107 | if gwm, ok := v.IsSystemMessage(); ok { 108 | fmt.Printf("%+v\n", gwm) 109 | continue 110 | } 111 | if gwm, ok := v.IsBuddylistChange(); ok { 112 | fmt.Println("blc", gwm) 113 | continue 114 | } 115 | fmt.Println(v) 116 | } 117 | 118 | } 119 | 120 | } 121 | -------------------------------------------------------------------------------- /webqq.sublime-project: -------------------------------------------------------------------------------- 1 | { 2 | "folders": 3 | [ 4 | { 5 | "follow_symlinks": true, 6 | "path": "." 7 | } 8 | ], 9 | "settings": 10 | { 11 | "GoSublime": 12 | { 13 | "env": 14 | { 15 | "GOPATH": "$GOPATH:$GS_GOPATH" 16 | } 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /webqq/client.go: -------------------------------------------------------------------------------- 1 | package webqq 2 | 3 | import ( 4 | "fmt" 5 | "github.com/astaxie/beego/logs" 6 | "math/rand" 7 | "net/http" 8 | "net/http/cookiejar" 9 | "net/url" 10 | "strconv" 11 | "strings" 12 | "time" 13 | . "webqq/tools" 14 | ) 15 | 16 | var lg *logs.BeeLogger 17 | 18 | type Client struct { 19 | id string 20 | password string 21 | vfwebqq string 22 | psessionid string 23 | clientid string 24 | ptwebqq string 25 | verifysession string 26 | client http.Client 27 | msgid int64 28 | *logs.BeeLogger 29 | } 30 | 31 | func init() { 32 | fmt.Printf(``) 33 | } 34 | 35 | func newClient(t time.Duration) http.Client { 36 | jar, err := cookiejar.New(nil) 37 | ErrHandle(err, `x`, `obtain_cookiejar`) 38 | 39 | return http.Client{ 40 | nil, 41 | nil, 42 | jar, 43 | t * time.Millisecond, 44 | } 45 | } 46 | 47 | func New(id, password string, timeout int, logchannellen int64) *Client { 48 | rd := rand.New(rand.NewSource(time.Now().UnixNano())) 49 | c := &Client{ 50 | id: id, 51 | password: password, 52 | clientid: strconv.Itoa(rd.Intn(90000000) + 10000000), 53 | client: newClient(time.Duration(timeout)), 54 | msgid: (rd.Int63n(9000) + 1000) * 10000, 55 | } 56 | c.BeeLogger = logs.NewLogger(logchannellen) 57 | lg = c.BeeLogger 58 | return c 59 | } 60 | 61 | func (qq *Client) Get(url string) (re *http.Response, err error) { 62 | return qq.get(url) 63 | } 64 | 65 | func (qq *Client) get(u string) (re *http.Response, err error) { 66 | defer func() { 67 | if e := recover(); e != nil { 68 | err = e.(error) 69 | } 70 | }() 71 | lg.Trace("\nGET: URL: %v", u) 72 | req, err := http.NewRequest("GET", u, nil) 73 | ErrHandle(err, `p`) 74 | 75 | req.Header.Add(`referer`, `http://d.web2.qq.com/proxy.html?v=20110331002&callback=2&id=3`) 76 | 77 | re, err = qq.client.Do(req) 78 | ErrHandle(err, `p`) 79 | 80 | if qq.ptwebqq == `` { 81 | for _, v := range re.Cookies() { 82 | if v.Name == `ptwebqq` { 83 | qq.ptwebqq = v.Value 84 | continue 85 | } 86 | if v.Name == "verifysession" { 87 | qq.verifysession = v.Value 88 | continue 89 | } 90 | } 91 | } 92 | 93 | qq.client.Jar.SetCookies(req.URL, re.Cookies()) 94 | return 95 | } 96 | 97 | func (qq *Client) postForm(u string, data url.Values) (re *http.Response, err error) { 98 | defer func() { 99 | if e := recover(); e != nil { 100 | err = e.(error) 101 | } 102 | }() 103 | lg.Trace("\nPOST: URL: %v\nDATA: %v", u, data.Encode()) 104 | req, err := http.NewRequest("POST", u, strings.NewReader(data.Encode())) 105 | ErrHandle(err, `p`) 106 | 107 | req.Header.Set("Content-Type", "application/x-www-form-urlencoded") 108 | 109 | if qq.client.Jar != nil { 110 | for _, cookie := range qq.client.Jar.Cookies(req.URL) { 111 | req.AddCookie(cookie) 112 | } 113 | } 114 | 115 | req.Header.Add(`referer`, `http://d.web2.qq.com/proxy.html?v=20110331002&callback=2&id=3`) 116 | 117 | re, err = qq.client.Do(req) 118 | ErrHandle(err, `p`) 119 | 120 | qq.client.Jar.SetCookies(req.URL, re.Cookies()) 121 | return 122 | } 123 | 124 | func (*Client) timeStamp() string { 125 | return fmt.Sprintf("%d", time.Now().UnixNano())[:13] 126 | } 127 | 128 | func (this *Client) SetPtWebqq(p string) { 129 | this.ptwebqq = p 130 | } 131 | -------------------------------------------------------------------------------- /webqq/getinfo.go: -------------------------------------------------------------------------------- 1 | package webqq 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | . "webqq/tools" 7 | "webqq/tools/simplejson" 8 | ) 9 | 10 | func (qq *Client) GetId(uin string, b ...*BuddyInfo) (str string, err error) { 11 | defer func() { 12 | if e := recover(); e != nil { 13 | err = e.(error) 14 | } 15 | }() 16 | if len(b) > 1 { 17 | return ``, fmt.Errorf(`too many buddyinfo objects`) 18 | } 19 | re, err := qq.get(`http://s.web2.qq.com/api/get_friend_uin2?tuin=` + 20 | uin + `&verifysession=` + qq.verifysession + `&type=1&code=&vfwebqq=` + 21 | qq.vfwebqq + 22 | `&t=` + qq.timeStamp()) 23 | 24 | if err != nil { 25 | panic(err) 26 | } 27 | defer re.Body.Close() 28 | j, err := simplejson.NewJson(ReadByte(re.Body)) 29 | if err != nil { 30 | panic(err) 31 | } 32 | acc, err := j.Get(`result`).Get(`account`).Int() 33 | if err != nil { 34 | panic(err) 35 | } 36 | str = fmt.Sprintf("%d", acc) 37 | if len(b) == 1 { 38 | b[0].Id = str 39 | } 40 | return 41 | } 42 | 43 | type BuddyInfo struct { 44 | Nick string `json:"nick"` 45 | markname string 46 | Face int `json:"face"` 47 | Id string `json:"id"` 48 | Uin int `json:"uin"` 49 | status string 50 | Birthday struct { 51 | Month int `json:"month"` 52 | Year int `json:"year"` 53 | Day int `json:"day"` 54 | } `json:"birthday"` 55 | CZodiac int `json:"shengxiao"` 56 | Occupation string `json:"occupation"` 57 | Phone string `json:"phone"` 58 | college string `json:"college"` 59 | Constel int `json:"constel"` 60 | Homepage string `json:"homepage"` 61 | Country string `json:"country"` 62 | City string `json:"city"` 63 | Email string `json:"email"` 64 | Province string `json:"province"` 65 | Gender string `json:"gender"` 66 | Mobile string `json:"mobile"` 67 | online int 68 | Vip_info int `json:"vip_info"` 69 | Personal string `json:"personal"` 70 | Stat int `json:"stat"` 71 | Blood int `json:"blood"` 72 | Client_type int `json:"client_type"` 73 | } 74 | 75 | type cc struct { 76 | Retcode int 77 | Result *BuddyInfo 78 | } 79 | 80 | func (qq *Client) GetInfo(uin string, b *BuddyInfo) error { 81 | re, err := qq.Get(`http://s.web2.qq.com/api/get_friend_info2?tuin=` + uin + 82 | `&verifysession=` + qq.verifysession + `&code=&vfwebqq=` + qq.vfwebqq + `&t=` + qq.timeStamp()) 83 | ErrHandle(err, `n`) 84 | byt := ReadByte(re.Body) 85 | err = json.Unmarshal(byt, &cc{0, b}) 86 | return err 87 | } 88 | 89 | // { 90 | // "retcode":0, 91 | // "result":{ 92 | // "face":600, 93 | // "birthday":{ 94 | // "month":1, 95 | // "year":1994, 96 | // "day":23 97 | // }, 98 | // "occupation":"", 99 | // "phone":"-", 100 | // "allow":4, 101 | // "college":"-", 102 | // "constel":1, 103 | // "blood":5, 104 | // "stat":20, 105 | // "homepage":"42.cnssuestc.org", 106 | // "country":"埃及", 107 | // "city":"阿斯旺", 108 | // "uiuin":"doomsplayer@gmail.com", 109 | // "personal":"", 110 | // "nick":"42", 111 | // "shengxiao":10, 112 | // "email":"", 113 | // "token":"262611e48b462f551b0415a18b66f407b2fd1a1d59f216d6", 114 | // "province":"", 115 | // "account":173165159, 116 | // "gender":"male", 117 | // "tuin":458076853, 118 | // "mobile":"" 119 | // } 120 | // } 121 | -------------------------------------------------------------------------------- /webqq/getlist.go: -------------------------------------------------------------------------------- 1 | package webqq 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "net/url" 7 | "strconv" 8 | . "webqq/tools" 9 | "webqq/tools/simplejson" 10 | ) 11 | 12 | func init() { 13 | fmt.Print("") 14 | } 15 | 16 | type Friend struct { 17 | Uin string 18 | Categories int 19 | Nick string 20 | Markname string 21 | Face int 22 | Online int 23 | Id string 24 | } 25 | 26 | type FriendMap map[string]*Friend 27 | type CataMap map[int]string 28 | 29 | type FriendList struct { 30 | FriendMap 31 | CataMap 32 | } 33 | 34 | func (qq *Client) GetFriendList() (flst *FriendList, err error) { 35 | defer func() { 36 | if e := recover(); e != nil { 37 | err = e.(error) 38 | } 39 | }() 40 | 41 | v := url.Values{} 42 | h, err := json.Marshal( 43 | map[string]string{ 44 | `h`: `hello`, 45 | `vfwebqq`: qq.vfwebqq, 46 | `hash`: hash(qq.id, qq.ptwebqq)}) 47 | 48 | v.Set(`r`, string(h)) 49 | 50 | re, err := qq.postForm(`http://s.web2.qq.com/api/get_user_friends2`, v) 51 | if err != nil { 52 | panic(err) 53 | } 54 | defer re.Body.Close() 55 | flst, err = friendListParser(ReadByte(re.Body)) 56 | if err != nil { 57 | panic(err) 58 | } 59 | for i := range flst.FriendMap { 60 | va, err := qq.GetId(i) 61 | if err != nil { 62 | panic(err) 63 | } 64 | flst.FriendMap[i].Id = va 65 | } 66 | 67 | return 68 | } 69 | 70 | func friendListParser(in []byte) (f *FriendList, err error) { 71 | js, err := simplejson.NewJson(in) 72 | if err != nil { 73 | return nil, err 74 | } 75 | f = &FriendList{} 76 | f.FriendMap = make(FriendMap) 77 | f.CataMap = make(CataMap) 78 | result := js.Get(`result`) 79 | for i, cat := 0, result.Get(`categories`); i < len(cat.MustArray()); i++ { 80 | f.CataMap[cat.GetIndex(i).Get(`index`).MustInt()] = cat.GetIndex(i).Get(`name`).MustString() 81 | } 82 | for i, frd := 0, result.Get(`friends`); i < len(frd.MustArray()); i++ { 83 | uin := fmt.Sprint(int64(frd.GetIndex(i).Get(`uin`).MustFloat64())) 84 | categories := frd.GetIndex(i).Get(`categories`).MustInt() 85 | f.FriendMap[uin] = &Friend{Uin: uin, Categories: categories} 86 | } 87 | for i, inf := 0, result.Get(`info`); i < len(inf.MustArray()); i++ { 88 | uin := fmt.Sprint(int64(inf.GetIndex(i).Get(`uin`).MustFloat64())) 89 | nick := inf.GetIndex(i).Get(`nick`).MustString() 90 | face := inf.GetIndex(i).Get(`face`).MustInt() 91 | flag := inf.GetIndex(i).Get(`flag`).MustInt() 92 | if ptr, ok := f.FriendMap[uin]; ok { 93 | ptr.Nick = nick 94 | ptr.Markname = nick 95 | ptr.Face = face 96 | ptr.Online = flag 97 | } 98 | } 99 | for i, mkn := 0, result.Get(`marknames`); i < len(mkn.MustArray()); i++ { 100 | markname := mkn.GetIndex(i).Get(`markname`).MustString() 101 | uin := fmt.Sprint(int64(mkn.GetIndex(i).Get(`uin`).MustFloat64())) 102 | if ptr, ok := f.FriendMap[uin]; ok { 103 | ptr.Markname = markname 104 | } 105 | } 106 | return 107 | } 108 | 109 | type b struct { 110 | s, e int 111 | } 112 | 113 | func nb(c, i int) b { 114 | return b{s: c | 0, e: i | 0} 115 | } 116 | func hash(is, a string) string { 117 | 118 | r := [4]int{} 119 | i, _ := strconv.Atoi(is) 120 | r[0] = i >> 24 & 255 121 | r[1] = i >> 16 & 255 122 | r[2] = i >> 8 & 255 123 | r[3] = i >> 0 & 255 124 | j := []int{} 125 | for e := 0; e < len(a); e++ { 126 | j = append(j, int(a[e])) 127 | } 128 | e := []b{} 129 | for e = append(e, nb(0, len(j)-1)); len(e) > 0; { 130 | var c = e[len(e)-1] 131 | e = e[:len(e)-1] 132 | if !(c.s >= c.e || c.s < 0 || c.e >= len(j)) { 133 | if c.s+1 == c.e { 134 | if j[c.s] > j[c.e] { 135 | j[c.s], j[c.e] = j[c.e], j[c.s] 136 | } 137 | } else { 138 | var f = j[c.s] 139 | var l = c.s 140 | var J = c.e 141 | for c.s < c.e { 142 | for c.s < c.e && j[c.e] >= f { 143 | c.e-- 144 | r[0] = (r[0] + 3) & 255 145 | } 146 | if c.s < c.e { 147 | j[c.s] = j[c.e] 148 | c.s++ 149 | r[1] = (r[1]*13 + 43) & 255 150 | } 151 | 152 | for c.s < c.e && j[c.s] <= f { 153 | c.s++ 154 | r[2] = (r[2] - 3) & 255 155 | } 156 | if c.s < c.e { 157 | j[c.e] = j[c.s] 158 | c.e-- 159 | r[3] = (r[0] ^ r[1] ^ r[2] ^ (r[3] + 1)) & 255 160 | } 161 | } 162 | 163 | j[c.s] = f 164 | e = append(e, nb(l, c.s-1)) 165 | e = append(e, nb(c.s+1, J)) 166 | } 167 | } 168 | } 169 | js := []string{"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F"} 170 | es := "" 171 | for c := 0; c < len(r); c++ { 172 | es += js[r[c]>>4&15] 173 | es += js[r[c]&15] 174 | } 175 | return es 176 | } 177 | 178 | /* 179 | { 180 | "retcode": 0, 181 | "result": { 182 | "friends": [ 183 | { 184 | "flag": 0, 185 | "uin": 1192173647, 186 | "categories": 0 187 | }, 188 | { 189 | "flag": 4, 190 | "uin": 2549892970, 191 | "categories": 1 192 | }, 193 | { 194 | "flag": 0, 195 | "uin": 4069820047, 196 | "categories": 0 197 | }, 198 | { 199 | "flag": 0, 200 | "uin": 490067261, 201 | "categories": 0 202 | }, 203 | { 204 | "flag": 0, 205 | "uin": 4185452572, 206 | "categories": 0 207 | }, 208 | { 209 | "flag": 8, 210 | "uin": 223290852, 211 | "categories": 2 212 | }, 213 | { 214 | "flag": 8, 215 | "uin": 1196002108, 216 | "categories": 2 217 | } 218 | ], 219 | "marknames": [ 220 | { 221 | "uin": 223290852, 222 | "markname": "沈超", 223 | "type": 0 224 | }, 225 | { 226 | "uin": 1196002108, 227 | "markname": "大头", 228 | "type": 0 229 | } 230 | ], 231 | "categories": [ 232 | { 233 | "index": 1, 234 | "sort": 1, 235 | "name": "道友" 236 | }, 237 | { 238 | "index": 2, 239 | "sort": 2, 240 | "name": "同学" 241 | } 242 | ], 243 | "vipinfo": [ 244 | { 245 | "vip_level": 0, 246 | "u": 1192173647, 247 | "is_vip": 0 248 | }, 249 | { 250 | "vip_level": 0, 251 | "u": 2549892970, 252 | "is_vip": 0 253 | }, 254 | { 255 | "vip_level": 2, 256 | "u": 4069820047, 257 | "is_vip": 1 258 | }, 259 | { 260 | "vip_level": 0, 261 | "u": 490067261, 262 | "is_vip": 0 263 | }, 264 | { 265 | "vip_level": 0, 266 | "u": 4185452572, 267 | "is_vip": 0 268 | }, 269 | { 270 | "vip_level": 0, 271 | "u": 223290852, 272 | "is_vip": 0 273 | }, 274 | { 275 | "vip_level": 0, 276 | "u": 1196002108, 277 | "is_vip": 0 278 | } 279 | ], 280 | "info": [ 281 | { 282 | "face": 600, 283 | "flag": 29884928, 284 | "nick": "42", 285 | "uin": 1192173647 286 | }, 287 | { 288 | "face": 12, 289 | "flag": 0, 290 | "nick": "火里栽莲", 291 | "uin": 2549892970 292 | }, 293 | { 294 | "face": 0, 295 | "flag": 13107718, 296 | "nick": "林选", 297 | "uin": 4069820047 298 | }, 299 | { 300 | "face": 336, 301 | "flag": 13107712, 302 | "nick": "度竹轻衣", 303 | "uin": 490067261 304 | }, 305 | { 306 | "face": 558, 307 | "flag": 524802, 308 | "nick": "Qrox", 309 | "uin": 4185452572 310 | }, 311 | { 312 | "face": 339, 313 | "flag": 13140480, 314 | "nick": "弎弍弌廿卅卌", 315 | "uin": 223290852 316 | }, 317 | { 318 | "face": 147, 319 | "flag": 8388608, 320 | "nick": "blue~beak", 321 | "uin": 1196002108 322 | } 323 | ] 324 | } 325 | }*/ 326 | -------------------------------------------------------------------------------- /webqq/log.go: -------------------------------------------------------------------------------- 1 | package webqq 2 | 3 | import ( 4 | "github.com/astaxie/beego/logs" 5 | ) 6 | 7 | var lg *logs.BeeLogger 8 | 9 | func init() { 10 | lg = logs.NewLogger(1000) 11 | lg.SetLogger("console", `{"level":2}`) 12 | lg.SetLogger("file", `{"filename":"log.log"}`) 13 | lg.SetLevel(logs.LevelTrace) 14 | } 15 | -------------------------------------------------------------------------------- /webqq/login.go: -------------------------------------------------------------------------------- 1 | package webqq 2 | 3 | import ( 4 | "crypto/md5" 5 | "encoding/hex" 6 | "encoding/json" 7 | "errors" 8 | "fmt" 9 | "math/rand" 10 | "net/url" 11 | // "os/exec" 12 | "regexp" 13 | "strings" 14 | . "webqq/tools" 15 | "webqq/tools/simplejson" 16 | ) 17 | 18 | type LoginRet struct { 19 | needPic bool 20 | PicAddr string 21 | p1 string 22 | p2 []byte 23 | pass string 24 | } 25 | 26 | func (s *LoginRet) SetVFCode(i string) error { 27 | if s.needPic { 28 | s.p1 = i 29 | return nil 30 | } else { 31 | return fmt.Errorf(`No Need For Setting Verify Code!`) 32 | } 33 | } 34 | 35 | func (s *LoginRet) gen() (vcl string, vcs string) { 36 | lg.Trace("generating vcl and vcs") 37 | 38 | sum1 := md5.Sum([]byte(s.pass)) 39 | sum2 := md5.Sum([]byte(append(sum1[:], s.p2...))) 40 | sum3 := md5.Sum([]byte(fmt.Sprintf("%X", sum2) + strings.ToUpper(s.p1))) 41 | 42 | vcl = fmt.Sprintf("%X", sum3) 43 | vcs = strings.ToUpper(s.p1) 44 | return 45 | } 46 | 47 | func (s *LoginRet) NeedPic() bool { 48 | return s.needPic 49 | } 50 | 51 | func (qq *Client) LoginStep1() (ret LoginRet, err error) { 52 | defer func() { 53 | if e := recover(); e != nil { 54 | err = e.(error) 55 | } 56 | }() 57 | 58 | re, err := qq.get(`https://ssl.ptlogin2.qq.com/check?uin=` + qq.id + `&appid=1003903&js_ver=10040&js_type=0&login_sig=glCTiV1*UGC58vTwRS3f-xyFDmTfq45dfLQxy2IMjw8BGt1UUldhM9fq2EXdSamn&u1=http%3A%2F%2Fweb2.qq.com%2Floginproxy.html&r=0.` + fmt.Sprint(rand.Int63n(10000000000000000))) 59 | ErrHandle(err, `p`) 60 | defer re.Body.Close() 61 | 62 | sBody := ReadString(re.Body) 63 | 64 | lg.Debug("ptui_checkVC is %v", sBody) 65 | 66 | p := regexp. 67 | MustCompile(`^ptui_checkVC\('(\d)','(.*?)','(.*?)', '(.*?)'\);$`). 68 | FindStringSubmatch(sBody) 69 | 70 | switch p[1] { 71 | case `0`: 72 | { 73 | lg.Info("No need for captcha") 74 | ret.needPic = false 75 | ret.p1 = p[2] 76 | } 77 | case `1`: 78 | { 79 | lg.Info("Need captcha") 80 | ret.needPic = true 81 | ret.PicAddr = `https://ssl.captcha.qq.com/getimage?aid=1003903&r=0.10382554663612781&uin=` + qq.id 82 | } 83 | default: 84 | { 85 | err = fmt.Errorf(`login error`) 86 | return 87 | } 88 | } 89 | 90 | cs := strings.Split(p[3], `\x`) 91 | for _, v := range cs { 92 | token, _ := hex.DecodeString(v) 93 | ret.p2 = append(ret.p2, token...) 94 | } 95 | 96 | ret.pass = qq.password 97 | return 98 | } 99 | 100 | func (qq *Client) LoginStep2(ret LoginRet) (err error) { 101 | defer func() { 102 | if e := recover(); e != nil { 103 | err = e.(error) 104 | } 105 | }() 106 | 107 | if ret.p1 == `` { 108 | lg.Warn("p1 not set") 109 | return fmt.Errorf(`Please Set Verify Code First`) 110 | } 111 | 112 | vcl, vcs := ret.gen() 113 | 114 | re, err := qq.get(`https://ssl.ptlogin2.qq.com/login?u=` + qq.id + `&p=` + vcl + `&verifycode=` + vcs + `&webqq_type=10&remember_uin=1&login2qq=1&aid=1003903&u1=http%3A%2F%2Fweb2.qq.com%2Floginproxy.html%3Flogin2qq%3D1%26webqq_type%3D10&h=1&ptredirect=0&ptlang=2052&daid=164&from_ui=1&pttype=1&dumy=&fp=loginerroralert&action=3-29-12615&mibao_css=m_webqq&t=1&g=1&js_type=0&js_ver=10040&login_sig=glCTiV1*UGC58vTwRS3f-xyFDmTfq45dfLQxy2IMjw8BGt1UUldhM9fq2EXdSamn`) 115 | if err != nil { 116 | panic(fmt.Errorf("第一次握手失败,网络错误: %v", err)) 117 | } 118 | defer re.Body.Close() 119 | 120 | sBody := ReadString(re.Body) 121 | lg.Debug("ptuiCB is %v", sBody) 122 | 123 | reg := regexp.MustCompile(`ptuiCB\('0','0','(.*)','0','登录成功!', '.*'\);`) 124 | 125 | if !reg.MatchString(sBody) { 126 | 127 | panic(errors.New(`第一次握手失败(密码错误?)`)) 128 | } 129 | 130 | ssl := reg.FindStringSubmatch(sBody) 131 | re, err = qq.get(ssl[1]) 132 | ErrHandle(err, `x`) 133 | defer re.Body.Close() 134 | 135 | v := url.Values{} 136 | v.Set(`clientid`, qq.clientid) 137 | v.Set(`psessionid`, `null`) 138 | 139 | c, err := json.Marshal( 140 | map[string]interface{}{ 141 | `status`: `online`, 142 | `ptwebqq`: qq.ptwebqq, 143 | `passwd_sig`: ``, 144 | `clientid`: qq.clientid, 145 | `psessionid`: nil}) 146 | v.Set(`r`, string(c)) 147 | 148 | re, err = qq.postForm(`http://d.web2.qq.com/channel/login2`, v) 149 | 150 | if err != nil { 151 | panic(fmt.Errorf("第二次握手失败:%v", err)) 152 | } 153 | defer re.Body.Close() 154 | 155 | retb := ReadByte(re.Body) 156 | lg.Debug("online info is %s", retb) 157 | 158 | js, err := simplejson.NewJson(retb) 159 | if err != nil { 160 | panic(err) 161 | } 162 | 163 | if i := js.Get(`retcode`).MustFloat64(); i != float64(0) { 164 | panic(fmt.Errorf("第二次握手失败,错误码:%v", i)) 165 | } 166 | 167 | qq.vfwebqq = js.Get(`result`).Get(`vfwebqq`).MustString() 168 | qq.psessionid = js.Get(`result`).Get(`psessionid`).MustString() 169 | return nil 170 | } 171 | -------------------------------------------------------------------------------- /webqq/pollmsg.go: -------------------------------------------------------------------------------- 1 | package webqq 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "time" 7 | //"net/http" 8 | "net/url" 9 | "strconv" 10 | . "webqq/tools" 11 | "webqq/tools/simplejson" 12 | ) 13 | 14 | func init() { 15 | fmt.Printf("") 16 | } 17 | 18 | var ELoginAgain = fmt.Errorf(`login again`) 19 | var ENOMSG = fmt.Errorf(`No msg for 10 minutes`) 20 | 21 | type PollType string 22 | 23 | const ( 24 | PT_Message = PollType("message") 25 | PT_SystemMessage = PollType("system_message") 26 | PT_KickMessage = PollType("kick_message") 27 | PT_GroupMessage = PollType("group_message") 28 | PT_InputStatus = PollType("input_status") 29 | PT_BuddiesStatusChange = PollType("buddies_status_change") 30 | PT_Tips = PollType("tips") 31 | PT_OK = PollType("ok") 32 | PT_InputNotify = PollType("input_notify") 33 | PT_SysGMessage = PollType("sys_g_msg") 34 | PT_GroupWebMessage = PollType("group_web_message") 35 | PT_BuddylistChange = PollType("buddylist_change") 36 | PT_DiscuMessage = PollType("discu_message") 37 | PT_SessMessage = PollType("sess_message") 38 | ) 39 | 40 | func ParseRawPoll(retu []byte) (ret []*PollMessage, err error) { 41 | defer func() { 42 | if e := recover(); e != nil { 43 | err = e.(error) 44 | } 45 | 46 | }() 47 | lg.Debug("parse raw poll: msg is %s", retu) 48 | js, err := simplejson.NewJson(retu) 49 | if err != nil { 50 | panic(fmt.Errorf("parse poll error,%v", err)) 51 | } 52 | 53 | retcode, err := js.Get(`retcode`).Int() 54 | 55 | if err != nil { 56 | panic(fmt.Errorf("parse poll error,%v", err)) 57 | } 58 | 59 | lg.Debug("ret code is %d", retcode) 60 | 61 | switch retcode { 62 | case 0: 63 | { 64 | 65 | result := js.Get(`result`) 66 | 67 | for i := 0; i < len(result.MustArray()); i++ { 68 | r := new(PollMessage) 69 | r.retCode = 0 70 | r.t = time.Now() 71 | 72 | resulti := result.GetIndex(i) 73 | 74 | poll_type := resulti.Get(`poll_type`).MustString("") 75 | 76 | r.value, _ = resulti.Get(`value`).MarshalJSON() 77 | 78 | switch PollType(poll_type) { 79 | case PT_Message: 80 | r.pollType = PT_Message 81 | case PT_SystemMessage: 82 | r.pollType = PT_SystemMessage 83 | // switch resulti.Get(`value`).Get(`type`).MustString() { 84 | // case `added_buddy_sig`: 85 | // { 86 | // t := int64(resulti.Get(`value`).Get(`time`).MustFloat64()) 87 | // fuin := strconv.FormatInt(int64(resulti.Get(`value`).Get(`from_uin`).MustFloat64()), 10) 88 | // ret = append(ret, &PollMessage{Type: `addBuddy`, FromUin: fuin, T: time.Unix(t, 0)}) 89 | // } 90 | // } 91 | case PT_KickMessage: 92 | r.pollType = PT_KickMessage //您的帐号在另一地点登录,您已被迫下线。 93 | // ret = append(ret, &PollMessage{Type: `kicked`, FromUin: `10000`, T: time.Now()}) 94 | case PT_GroupMessage: 95 | r.pollType = PT_GroupMessage 96 | case PT_InputStatus: 97 | r.pollType = PT_InputStatus 98 | case PT_BuddiesStatusChange: 99 | r.pollType = PT_BuddiesStatusChange 100 | case PT_Tips: 101 | r.pollType = PT_Tips 102 | case PT_InputNotify: 103 | r.pollType = PT_InputNotify 104 | case PT_OK: 105 | r.pollType = PT_OK 106 | case PT_SysGMessage: 107 | r.pollType = PT_SysGMessage 108 | case PT_GroupWebMessage: 109 | r.pollType = PT_GroupWebMessage 110 | case PT_BuddylistChange: 111 | r.pollType = PT_BuddylistChange 112 | case PT_DiscuMessage: 113 | r.pollType = PT_DiscuMessage 114 | case PT_SessMessage: 115 | r.pollType = PT_SessMessage 116 | default: 117 | lg.Critical("doesn't expected poll type", poll_type) 118 | } 119 | ret = append(ret, r) 120 | } 121 | } 122 | case 102: 123 | { 124 | r := new(PollMessage) 125 | r.retCode = 102 126 | r.t = time.Now() 127 | r.errMsg = js.Get("errmsg").MustString("") 128 | ret = append(ret, r) 129 | } 130 | case 116: 131 | { 132 | 133 | p := js.Get("p").MustString("") 134 | 135 | r := new(PollMessage) 136 | r.retCode = 116 137 | r.t = time.Now() 138 | r.value = []byte(p) 139 | ret = append(ret, r) 140 | } 141 | case 103: 142 | { 143 | r := new(PollMessage) 144 | r.retCode = 103 145 | r.t = time.Now() 146 | r.errMsg = js.Get("errmsg").MustString("") 147 | ret = append(ret, r) 148 | } 149 | case 121: 150 | { 151 | r := new(PollMessage) 152 | r.retCode = 121 153 | r.t = time.Now() 154 | r.errMsg = js.Get("errmsg").MustString("") 155 | ret = append(ret, r) 156 | } 157 | case 100006: 158 | { 159 | r := new(PollMessage) 160 | r.retCode = 100006 161 | r.t = time.Now() 162 | r.errMsg = js.Get("errmsg").MustString("") 163 | ret = append(ret, r) 164 | } 165 | case 108: 166 | { 167 | r := new(PollMessage) 168 | r.retCode = 108 169 | r.t = time.Now() 170 | r.errMsg = js.Get("errmsg").MustString("") 171 | ret = append(ret, r) 172 | } 173 | case 112: 174 | { 175 | r := new(PollMessage) 176 | r.retCode = 112 177 | r.t = time.Now() 178 | r.errMsg = js.Get("errmsg").MustString("") 179 | ret = append(ret, r) 180 | } 181 | default: 182 | { 183 | err = fmt.Errorf("unknown ret code:%v", retcode) 184 | } 185 | } 186 | return 187 | } 188 | 189 | func (qq *Client) RawPoll() (retu []byte, err error) { 190 | defer func() { 191 | if e := recover(); e != nil { 192 | err = e.(error) 193 | } 194 | 195 | }() 196 | 197 | c, _ := json.Marshal(map[string]interface{}{ 198 | `clientid`: qq.clientid, 199 | `psessionid`: qq.psessionid, 200 | `key`: 0, 201 | `ids`: [0]interface{}{}}) 202 | 203 | v := url.Values{} 204 | v.Set(`clientid`, qq.clientid) 205 | v.Set(`psessionid`, qq.psessionid) 206 | v.Set(`r`, string(c)) 207 | 208 | re, err := qq.postForm(`http://d.web2.qq.com/channel/poll2`, v) 209 | ErrHandle(err, `p`) 210 | defer re.Body.Close() 211 | retu = ReadByte(re.Body) 212 | lg.Debug("poll raw msg is %s", retu) 213 | return 214 | 215 | } 216 | 217 | type PollMessage struct { 218 | retCode int 219 | //if retcode == 0 220 | pollType PollType 221 | value []byte 222 | 223 | //if retcode != 0 224 | errMsg string 225 | 226 | //Public 227 | t time.Time 228 | } 229 | 230 | func (this *PollMessage) RetCode() int { 231 | return this.retCode 232 | } 233 | func (this *PollMessage) PollType() (PollType, error) { 234 | if this.retCode != 0 { 235 | return ``, fmt.Errorf("ret code is not 0") 236 | } 237 | return this.pollType, nil 238 | } 239 | 240 | type BuddyMessage struct { 241 | PollType PollType 242 | MessageId int64 243 | FromUin string 244 | ToUin string 245 | MessageId2 int64 246 | MessageType int64 247 | ReplyIp int64 248 | Time time.Time 249 | Font struct { 250 | Size int 251 | Color string 252 | Style [3]int 253 | Name string 254 | } 255 | Content []string 256 | } 257 | 258 | func (this *PollMessage) IsBuddyMessage() (ret *BuddyMessage, is bool) { 259 | if this.retCode != 0 || this.pollType != PT_Message { 260 | return nil, false 261 | } 262 | 263 | js, err := simplejson.NewJson(this.value) 264 | if err != nil { 265 | return nil, false 266 | } 267 | fromUin := strconv.FormatInt(int64(js.Get(`from_uin`).MustFloat64()), 10) 268 | toUin := strconv.FormatInt(int64(js.Get(`to_uin`).MustFloat64()), 10) 269 | msgId := int64(js.Get("msg_id").MustFloat64()) 270 | msgId2 := int64(js.Get("msg_id2").MustFloat64()) 271 | msgType := int64(js.Get("msg_type").MustFloat64()) 272 | replyIp := int64(js.Get("reply_ip").MustFloat64()) 273 | t := time.Unix(int64(js.Get("time").MustFloat64()), 0) 274 | contentjs := js.Get("content") 275 | fontjs := contentjs.GetIndex(0).GetIndex(1) 276 | fontSize := int(fontjs.Get("size").MustFloat64()) 277 | color := fontjs.Get("color").MustString("000000") 278 | fontName := fontjs.Get("name").MustString("") 279 | content := []string{} 280 | for i := 1; i < len(contentjs.MustArray([]interface{}{})); i++ { 281 | content = append(content, fmt.Sprint(contentjs.GetIndex(i).MustString("不支持文字以外的消息"))) 282 | 283 | } 284 | 285 | ret = new(BuddyMessage) 286 | ret.FromUin = fromUin 287 | ret.ToUin = toUin 288 | ret.MessageId = msgId 289 | ret.MessageId2 = msgId2 290 | ret.MessageType = msgType 291 | ret.ReplyIp = replyIp 292 | ret.Time = t 293 | ret.PollType, _ = this.PollType() 294 | ret.Font.Color = color 295 | ret.Font.Name = fontName 296 | ret.Font.Size = fontSize 297 | ret.Font.Style = [3]int{0, 0, 0} 298 | ret.Content = content 299 | return ret, true 300 | 301 | } 302 | 303 | type BuddyStatusChange struct { 304 | Uin string 305 | Status string 306 | ClientType int 307 | } 308 | 309 | func (this *PollMessage) IsBuddyStatusChange() (ret *BuddyStatusChange, is bool) { 310 | 311 | if this.retCode != 0 || this.pollType != PT_BuddiesStatusChange { 312 | return nil, false 313 | } 314 | js, err := simplejson.NewJson([]byte(this.value)) 315 | if err != nil { 316 | return nil, false 317 | } 318 | uin := strconv.FormatInt(int64(js.Get(`uin`).MustFloat64()), 10) 319 | status := js.Get("status").MustString("offline") 320 | clientType := int(js.Get("client_type").MustFloat64()) 321 | 322 | ret = new(BuddyStatusChange) 323 | ret.Uin = uin 324 | ret.Status = status 325 | ret.ClientType = clientType 326 | return ret, true 327 | } 328 | 329 | func (this *PollMessage) IsNothing() bool { 330 | if this.retCode == 102 { 331 | return true 332 | } 333 | return false 334 | } 335 | func (this *PollMessage) IsKick() bool { 336 | if this.retCode != 0 || this.pollType != PT_KickMessage { 337 | return false 338 | } 339 | return true 340 | } 341 | 342 | type GroupMessage struct { 343 | PollType PollType 344 | MessageId int64 345 | FromUin string 346 | ToUin string 347 | MessageId2 int64 348 | MessageType int64 349 | ReplyIp int64 350 | GroupCode string 351 | SendUin string 352 | Seq int64 353 | Time time.Time 354 | InfoSeq int64 355 | Font struct { 356 | Size int 357 | Color string 358 | Style [3]int 359 | Name string 360 | } 361 | Content []string 362 | } 363 | 364 | func (this *PollMessage) IsGroupMessage() (*GroupMessage, bool) { 365 | 366 | if this.retCode != 0 || this.pollType != PT_GroupMessage { 367 | return nil, false 368 | } 369 | 370 | js, err := simplejson.NewJson(this.value) 371 | if err != nil { 372 | return nil, false 373 | } 374 | fromUin := strconv.FormatInt(int64(js.Get(`from_uin`).MustFloat64()), 10) 375 | toUin := strconv.FormatInt(int64(js.Get(`to_uin`).MustFloat64()), 10) 376 | msgId := int64(js.Get("msg_id").MustFloat64()) 377 | msgId2 := int64(js.Get("msg_id2").MustFloat64()) 378 | msgType := int64(js.Get("msg_type").MustFloat64()) 379 | replyIp := int64(js.Get("reply_ip").MustFloat64()) 380 | groupCode := strconv.FormatInt(int64(js.Get("group_code").MustFloat64()), 10) 381 | sendUin := strconv.FormatInt(int64(js.Get("send_uin").MustFloat64()), 10) 382 | seq := int64(js.Get("seq").MustFloat64()) 383 | infoSeq := int64(js.Get("info_seq").MustFloat64()) 384 | t := time.Unix(int64(js.Get("time").MustFloat64()), 0) 385 | contentjs := js.Get("content") 386 | fontjs := contentjs.GetIndex(0).GetIndex(1) 387 | fontSize := int(fontjs.Get("size").MustFloat64()) 388 | color := fontjs.Get("color").MustString("000000") 389 | fontName := fontjs.Get("name").MustString("") 390 | content := []string{} 391 | for i := 1; i < len(contentjs.MustArray([]interface{}{})); i++ { 392 | content = append(content, fmt.Sprint(contentjs.GetIndex(i).MustString("不支持文字以外的消息"))) 393 | 394 | } 395 | 396 | ret := new(GroupMessage) 397 | ret.FromUin = fromUin 398 | ret.ToUin = toUin 399 | ret.MessageId = msgId 400 | ret.MessageId2 = msgId2 401 | ret.MessageType = msgType 402 | ret.ReplyIp = replyIp 403 | ret.InfoSeq = infoSeq 404 | ret.Seq = seq 405 | ret.SendUin = sendUin 406 | ret.GroupCode = groupCode 407 | ret.Time = t 408 | ret.PollType, _ = this.PollType() 409 | ret.Font.Color = color 410 | ret.Font.Name = fontName 411 | ret.Font.Size = fontSize 412 | ret.Font.Style = [3]int{0, 0, 0} 413 | ret.Content = content 414 | return ret, true 415 | 416 | } 417 | 418 | func (this *PollMessage) IsNewPtwebqq() (string, bool) { 419 | if this.retCode == 116 { 420 | return string(this.value), true 421 | } else { 422 | return ``, false 423 | } 424 | } 425 | 426 | func (this *PollMessage) IsOffline() bool { 427 | if this.retCode == 108 && this.retCode == 112 { 428 | return true 429 | } else { 430 | return false 431 | } 432 | } 433 | 434 | func (this *PollMessage) IsTips() (ret string, is bool) { 435 | if this.retCode != 0 || this.pollType != PT_Tips { 436 | return ``, false 437 | } 438 | return string(this.value), true 439 | } 440 | func (this *PollMessage) IsSystemMessage() (ret string, is bool) { 441 | if this.retCode != 0 || this.pollType != PT_SystemMessage { 442 | return ``, false 443 | } 444 | return string(this.value), true 445 | } 446 | 447 | // {"msg_id":6026,"from_uin":1502113816,"to_uin":173165159,"msg_id2":497528, 448 | // "msg_type":45,"reply_ip":180028749,"group_code":2288613488,"group_type":1, 449 | // "ver":1,"send_uin":291028157, 450 | // "xml":"\u0001\u0000\u0001 \u0000\u0018m\u00BA\u0000\u000F\u00C8\u02FC\u00E4\u00B5\u00DA\u04BB\u00C7\u00E9-\u00B9\u00A8\u00ABh\u0000\u00EEtencent://miniplayer/?cmd=1\u0026fuin=125846471\u0026frienduin=125846471\u0026groupid=2188573954\u0026groupcode=198573954\u0026action=\u0027accept\u0027\u0026mdlurl=\u0027http://scenecgi.chatshow.qq.com/fcgi-bin/gm_qry_music_info.fcg?songcount=1\u0026songidlist=1600954\u0026version=207\u0026cmd=1\u0027\u0000\u00EEtencent://miniplayer/?cmd=1\u0026fuin=125846471\u0026frienduin=125846471\u0026groupid=2188573954\u0026groupcode=198573954\u0026action=\u0027refuse\u0027\u0026mdlurl=\u0027http://scenecgi.chatshow.qq.com/fcgi-bin/gm_qry_music_info.fcg?songcount=1\u0026songidlist=1600954\u0026version=207\u0026cmd=1\u0027"}}]} 451 | func (this *PollMessage) IsGroupWebMessage() (ret string, is bool) { 452 | if this.retCode != 0 || this.pollType != PT_GroupWebMessage { 453 | return ``, false 454 | } 455 | //TODO 456 | return string(this.value), true 457 | } 458 | func (this *PollMessage) IsBuddylistChange() (ret string, is bool) { 459 | if this.retCode != 0 || this.pollType != PT_BuddylistChange { 460 | return ``, false 461 | } 462 | return string(this.value), true 463 | } 464 | 465 | type SysGMessage struct { 466 | AdminUin string 467 | FromUin string 468 | Gcode string 469 | Message string 470 | MessageId int64 471 | MessageId2 int64 472 | MessageType int64 473 | ReplyIp int64 474 | TGcode string 475 | ToUin string 476 | Type string 477 | } 478 | 479 | func (this *PollMessage) IsSysGMessage() (ret *SysGMessage, is bool) { 480 | if this.retCode != 0 || this.pollType != PT_SysGMessage { 481 | return nil, false 482 | } 483 | ret = new(SysGMessage) 484 | js, _ := simplejson.NewJson(this.value) 485 | ret.AdminUin = fmt.Sprint(int64(js.Get("admin_uin").MustFloat64())) 486 | ret.FromUin = fmt.Sprint(int64(js.Get("from_uin").MustFloat64())) 487 | ret.Gcode = fmt.Sprint(int64(js.Get("gcode").MustFloat64())) 488 | ret.Message = js.Get("msg").MustString("") 489 | ret.MessageId = int64(js.Get("msg_id").MustFloat64()) 490 | ret.MessageId2 = int64(js.Get("msg_id2").MustFloat64()) 491 | ret.MessageType = int64(js.Get("msg_type").MustFloat64()) 492 | ret.ReplyIp = int64(js.Get("admin_uin").MustFloat64()) 493 | ret.TGcode = fmt.Sprint(int64(js.Get("t_gcode").MustFloat64())) 494 | ret.ToUin = fmt.Sprint(int64(js.Get("to_uin").MustFloat64())) 495 | ret.Type = js.Get("type").MustString("") 496 | //{"admin_uin":1.366473281e+09,"from_uin":4.217094352e+09,"gcode":3.762527505e+09, 497 | //"msg":"","msg_id":32284,"msg_id2":22284,"msg_type":36,"reply_ip":1.76488537e+08, 498 | //"t_gcode":2.13871351e+08,"to_uin":1.73165159e+08,"type":"group_request_join_agree"} 499 | 500 | return ret, true 501 | } 502 | 503 | type SessMessage struct { 504 | MessageId int64 505 | FromUin string 506 | ToUin string 507 | MessageId2 int64 508 | MessageType int64 509 | ReplyIp int64 510 | Time time.Time 511 | Id string 512 | Ruin string 513 | ServiceType int64 514 | Flags struct { 515 | Text bool 516 | Pic bool 517 | File bool 518 | Audio bool 519 | Video bool 520 | } 521 | Font struct { 522 | Size int 523 | Color string 524 | Style [3]int 525 | Name string 526 | } 527 | Content []string 528 | } 529 | 530 | func (this *PollMessage) IsSessMessage() (ret *SessMessage, is bool) { 531 | if this.retCode != 0 || this.pollType != PT_SessMessage { 532 | return nil, false 533 | } 534 | js, _ := simplejson.NewJson(this.value) 535 | ret = new(SessMessage) 536 | ret.MessageId = int64(js.Get("msg_id").MustFloat64()) 537 | ret.MessageId2 = int64(js.Get("msg_id2").MustFloat64()) 538 | ret.ToUin = fmt.Sprint(int64(js.Get("to_uin").MustFloat64())) 539 | ret.FromUin = fmt.Sprint(int64(js.Get("from_uin").MustFloat64())) 540 | ret.MessageType = int64(js.Get("msg_type").MustFloat64()) 541 | ret.ReplyIp = int64(js.Get("admin_uin").MustFloat64()) 542 | ret.Time = time.Unix(int64(js.Get("time").MustFloat64()), 0) 543 | ret.Id = fmt.Sprint(int64(js.Get("id").MustFloat64())) 544 | ret.Ruin = fmt.Sprint(int64(js.Get("ruin").MustFloat64())) 545 | ret.ServiceType = int64(js.Get("service_type").MustFloat64()) 546 | 547 | ret.Flags.Text, _ = strconv.ParseBool(fmt.Sprint(js.Get("flags").Get("text").MustInt(1))) 548 | ret.Flags.Audio, _ = strconv.ParseBool(fmt.Sprint(js.Get("flags").Get("audio").MustInt(1))) 549 | ret.Flags.File, _ = strconv.ParseBool(fmt.Sprint(js.Get("flags").Get("file").MustInt(1))) 550 | ret.Flags.Pic, _ = strconv.ParseBool(fmt.Sprint(js.Get("flags").Get("pic").MustInt(1))) 551 | ret.Flags.Video, _ = strconv.ParseBool(fmt.Sprint(js.Get("flags").Get("video").MustInt(1))) 552 | 553 | ret.Font.Size = js.Get("content").GetIndex(0).GetIndex(1).Get("size").MustInt(15) 554 | 555 | ret.Font.Color = js.Get("content").GetIndex(0).GetIndex(1).Get("color").MustString("000000") 556 | ret.Font.Name = js.Get("content").GetIndex(0).GetIndex(1).Get("name").MustString("宋体") 557 | for i := 1; i < len(js.Get("content").MustArray([]interface{}{})); i++ { 558 | ret.Content = append(ret.Content, fmt.Sprint(js.Get("content").GetIndex(i))) 559 | } 560 | return 561 | } 562 | -------------------------------------------------------------------------------- /webqq/sendbuddymsg.go: -------------------------------------------------------------------------------- 1 | package webqq 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "net/url" 7 | "strconv" 8 | . "webqq/tools" 9 | "webqq/tools/simplejson" 10 | ) 11 | 12 | func (qq *Client) buddyMsgStructer(uiuin string, msg_id int64, msg, fontname, fontsize, fontcolor string, fontstyle [3]int) url.Values { 13 | uin, _ := strconv.Atoi(uiuin) 14 | v := url.Values{} 15 | v.Set(`clientid`, qq.clientid) 16 | v.Set(`psessionid`, qq.psessionid) 17 | ms := [2]interface{}{ 18 | msg, 19 | [2]interface{}{ 20 | "font", 21 | map[string]interface{}{ 22 | "name": fontname, 23 | "size": fontsize, 24 | "style": fontstyle, 25 | "color": fontcolor}}} 26 | byts, _ := json.Marshal(ms) 27 | m := map[string]interface{}{ 28 | "to": uin, 29 | "face": 0, 30 | "content": string(byts), 31 | "msg_id": msg_id, 32 | "clientid": qq.clientid, 33 | "psessionid": qq.psessionid, 34 | } 35 | 36 | byts, _ = json.Marshal(m) 37 | v.Set(`r`, string(byts)) 38 | return v 39 | } 40 | 41 | func (qq *Client) SendBuddyMsgEasy(uin string, msg_id int64, msg string) (err error) { 42 | return qq.SendBuddyMsg(uin, msg_id, msg, `宋体`, `15`, `000000`, [3]int{0, 0, 0}) 43 | } 44 | 45 | func (qq *Client) SendBuddyMsg(uin string, msg_id int64, msg, fontname, fontsize, fontcolor string, fontstyle [3]int) (err error) { 46 | defer func() { 47 | if e := recover(); e != nil { 48 | err = e.(error) 49 | } 50 | qq.msgid++ 51 | }() 52 | v := qq.buddyMsgStructer(uin, qq.msgid, msg, fontname, fontsize, fontcolor, fontstyle) 53 | re, err := qq.postForm(`http://d.web2.qq.com/channel/send_buddy_msg2`, v) 54 | if err != nil { 55 | panic(err) 56 | } 57 | ret, err := simplejson.NewJson(ReadByte(re.Body)) 58 | if err != nil { 59 | panic(err) 60 | } 61 | if i := ret.Get(`retcode`).MustInt(); i == 0 { 62 | return nil 63 | } else { 64 | panic(fmt.Errorf("发送个人消息:%v 失败,错误代码:%v", msg, i)) 65 | } 66 | return 67 | } 68 | -------------------------------------------------------------------------------- /webqq/sendgroupmsg.go: -------------------------------------------------------------------------------- 1 | package webqq 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "net/url" 7 | "strconv" 8 | . "webqq/tools" 9 | "webqq/tools/simplejson" 10 | ) 11 | 12 | func (qq *Client) groupMsgStructer(uiuin string, msg_id int64, msg, fontname, fontsize, fontcolor string, fontstyle [3]int) url.Values { 13 | uin, _ := strconv.Atoi(uiuin) 14 | v := url.Values{} 15 | v.Set(`clientid`, qq.clientid) 16 | v.Set(`psessionid`, qq.psessionid) 17 | 18 | ms := []interface{}{ 19 | msg, 20 | [2]interface{}{ 21 | "font", 22 | map[string]interface{}{ 23 | "name": fontname, 24 | "size": fontsize, 25 | "style": fontstyle, 26 | "color": fontcolor}}} 27 | byts, _ := json.Marshal(ms) 28 | 29 | m := map[string]interface{}{ 30 | "group_uin": uin, 31 | "content": string(byts), 32 | "msg_id": msg_id, 33 | "clientid": qq.clientid, 34 | "psessionid": qq.psessionid, 35 | } 36 | 37 | byts, _ = json.Marshal(m) 38 | v.Set(`r`, string(byts)) 39 | return v 40 | } 41 | 42 | func (qq *Client) SendGroupMsgEasy(uin string, msg_id int64, msg string) (err error) { 43 | return qq.SendGroupMsg(uin, msg_id, msg, `宋体`, `15`, `000000`, [3]int{0, 0, 0}) 44 | } 45 | 46 | func (qq *Client) SendGroupMsg(uin string, msg_id int64, msg, fontname, fontsize, fontcolor string, fontstyle [3]int) (err error) { 47 | defer func() { 48 | if e := recover(); e != nil { 49 | err = e.(error) 50 | } 51 | qq.msgid++ 52 | }() 53 | fmt.Println(qq.msgid) 54 | v := qq.groupMsgStructer(uin, qq.msgid, msg, fontname, fontsize, fontcolor, fontstyle) 55 | re, err := qq.postForm(`http://d.web2.qq.com/channel/send_qun_msg2`, v) 56 | 57 | if err != nil { 58 | panic(err) 59 | } 60 | ret, err := simplejson.NewJson(ReadByte(re.Body)) 61 | if err != nil { 62 | panic(err) 63 | } 64 | if i := ret.Get(`retcode`).MustInt(); i == 0 { 65 | return nil 66 | } else { 67 | panic(fmt.Errorf("发送群消息:%v 失败,错误代码:%v", msg, i)) 68 | } 69 | return 70 | } 71 | 72 | //r={"content":"[[\"font\",{\"name\":\"宋体\",\"size\":\"10\",\"style\":[0,0,0],\"color\":\"000000\"}]]"} 73 | 74 | //r={"content":"[[\"font\",{\"color\":\"000000\",\"name\":\"宋体\",\"size\":\"15\",\"style\":[0,0,0]}]]","face":0} 75 | 76 | // { 77 | // "clientid": "66979012", 78 | // "psessionid": "8368046764001d636f6e6e7365727665725f77656271714031302e3133332e34312e383400001a5e00000163026e04008a26454d6d0000000a40385775676e7446777a6d000000286798f9b9ed9385e4a86a8933ea02994fd55e386d7fa95b8190110c82f80416edbe540258ebd1980c", 79 | // "key": 0, 80 | // "ids": [ 81 | // "58705" 82 | // ] 83 | // }, 84 | // { 85 | // "group_uin": 1461871639, 86 | // "content": "[\"l\",[\"font\",{\"name\":\"宋体\",\"size\":\"10\",\"style\":[0,0,0],\"color\":\"000000\"}]]", 87 | // "msg_id": 89790003, 88 | // "clientid": "66979012", 89 | // "psessionid": "8368046764001d636f6e6e7365727665725f77656271714031302e3133332e34312e383400001a5e00000163026e04008a26454d6d0000000a40385775676e7446777a6d000000286798f9b9ed9385e4a86a8933ea02994fd55e386d7fa95b8190110c82f80416edbe540258ebd1980c" 90 | // } 91 | -------------------------------------------------------------------------------- /webqq/tools/simplejson/.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | install: 3 | - go get github.com/bmizerany/assert 4 | -------------------------------------------------------------------------------- /webqq/tools/simplejson/simplejson.go: -------------------------------------------------------------------------------- 1 | package simplejson 2 | 3 | import ( 4 | "bytes" 5 | "encoding/json" 6 | "errors" 7 | "io/ioutil" 8 | "log" 9 | "strings" 10 | ) 11 | 12 | // returns the current implementation version 13 | func Version() string { 14 | return "0.5" 15 | 16 | } 17 | 18 | type Json struct { 19 | data interface{} 20 | } 21 | 22 | // 解析json文件,过滤掉注释(只支持注释在单独一行,以"//"注释) 23 | func ParseFile(filename string) (*Json, error) { 24 | stream, err := ioutil.ReadFile(filename) 25 | if err != nil { 26 | return nil, err 27 | } 28 | content := string(stream) 29 | var builder bytes.Buffer 30 | lines := strings.Split(content, "\n") 31 | for _, line := range lines { 32 | line = strings.TrimSpace(line) 33 | if line == "" || strings.HasPrefix(line, "//") { 34 | continue 35 | } 36 | builder.WriteString(line) 37 | } 38 | return NewJson(builder.Bytes()) 39 | } 40 | 41 | // NewJson returns a pointer to a new `Json` object 42 | // after unmarshaling `body` bytes 43 | func NewJson(body []byte) (*Json, error) { 44 | j := new(Json) 45 | err := j.UnmarshalJSON(body) 46 | if err != nil { 47 | return nil, err 48 | } 49 | return j, nil 50 | } 51 | 52 | // Encode returns its marshaled data as `[]byte` 53 | func (j *Json) Encode() ([]byte, error) { 54 | return j.MarshalJSON() 55 | } 56 | 57 | // Implements the json.Unmarshaler interface. 58 | func (j *Json) UnmarshalJSON(p []byte) error { 59 | return json.Unmarshal(p, &j.data) 60 | } 61 | 62 | // Implements the json.Marshaler interface. 63 | func (j *Json) MarshalJSON() ([]byte, error) { 64 | return json.Marshal(&j.data) 65 | } 66 | 67 | // Get returns a pointer to a new `Json` object 68 | // for `key` in its `map` representation 69 | // 70 | // useful for chaining operations (to traverse a nested JSON): 71 | // js.Get("top_level").Get("dict").Get("value").Int() 72 | func (j *Json) Get(key string) *Json { 73 | m, err := j.Map() 74 | if err == nil { 75 | if val, ok := m[key]; ok { 76 | return &Json{val} 77 | } 78 | } 79 | return &Json{nil} 80 | } 81 | 82 | // GetStringSlice returns slice of string. 83 | func (j *Json) GetStringSlice(key string) []string { 84 | currentJson := j.Get(key) 85 | a, err := currentJson.Array() 86 | stringSlice := make([]string, len(a)) 87 | if err == nil { 88 | for i, v := range a { 89 | if tmp, ok := v.(string); ok { 90 | stringSlice[i] = tmp 91 | } 92 | } 93 | } 94 | return stringSlice 95 | } 96 | 97 | // GetIndex resturns a pointer to a new `Json` object 98 | // for `index` in its `array` representation 99 | // 100 | // this is the analog to Get when accessing elements of 101 | // a json array instead of a json object: 102 | // js.Get("top_level").Get("array").GetIndex(1).Get("key").Int() 103 | func (j *Json) GetIndex(index int) *Json { 104 | a, err := j.Array() 105 | if err == nil { 106 | if len(a) > index { 107 | return &Json{a[index]} 108 | } 109 | } 110 | return &Json{nil} 111 | } 112 | 113 | // CheckGet returns a pointer to a new `Json` object and 114 | // a `bool` identifying success or failure 115 | // 116 | // useful for chained operations when success is important: 117 | // if data, ok := js.Get("top_level").CheckGet("inner"); ok { 118 | // log.Println(data) 119 | // } 120 | func (j *Json) CheckGet(key string) (*Json, bool) { 121 | m, err := j.Map() 122 | if err == nil { 123 | if val, ok := m[key]; ok { 124 | return &Json{val}, true 125 | } 126 | } 127 | return nil, false 128 | } 129 | 130 | // Map type asserts to `map` 131 | func (j *Json) Map() (map[string]interface{}, error) { 132 | if m, ok := (j.data).(map[string]interface{}); ok { 133 | return m, nil 134 | } 135 | return nil, errors.New("type assertion to map[string]interface{} failed") 136 | } 137 | 138 | // Array type asserts to an `array` 139 | func (j *Json) Array() ([]interface{}, error) { 140 | if a, ok := (j.data).([]interface{}); ok { 141 | return a, nil 142 | } 143 | return nil, errors.New("type assertion to []interface{} failed") 144 | } 145 | 146 | // Bool type asserts to `bool` 147 | func (j *Json) Bool() (bool, error) { 148 | if s, ok := (j.data).(bool); ok { 149 | return s, nil 150 | } 151 | return false, errors.New("type assertion to bool failed") 152 | } 153 | 154 | // String type asserts to `string` 155 | func (j *Json) String() (string, error) { 156 | if s, ok := (j.data).(string); ok { 157 | return s, nil 158 | } 159 | return "", errors.New("type assertion to string failed") 160 | } 161 | 162 | // Float64 type asserts to `float64` 163 | func (j *Json) Float64() (float64, error) { 164 | if i, ok := (j.data).(float64); ok { 165 | return i, nil 166 | } 167 | return -1, errors.New("type assertion to float64 failed") 168 | } 169 | 170 | // Int type asserts to `float64` then converts to `int` 171 | func (j *Json) Int() (int, error) { 172 | if f, ok := (j.data).(float64); ok { 173 | return int(f), nil 174 | } 175 | 176 | return -1, errors.New("type assertion to float64 failed") 177 | } 178 | 179 | // Int type asserts to `float64` then converts to `int64` 180 | func (j *Json) Int64() (int64, error) { 181 | if f, ok := (j.data).(float64); ok { 182 | return int64(f), nil 183 | } 184 | 185 | return -1, errors.New("type assertion to float64 failed") 186 | } 187 | 188 | // Bytes type asserts to `[]byte` 189 | func (j *Json) Bytes() ([]byte, error) { 190 | if s, ok := (j.data).(string); ok { 191 | return []byte(s), nil 192 | } 193 | return nil, errors.New("type assertion to []byte failed") 194 | } 195 | 196 | // MustString guarantees the return of a `string` (with optional default) 197 | // 198 | // useful when you explicitly want a `string` in a single value return context: 199 | // myFunc(js.Get("param1").MustString(), js.Get("optional_param").MustString("my_default")) 200 | func (j *Json) MustString(args ...string) string { 201 | var def string 202 | 203 | switch len(args) { 204 | case 0: 205 | break 206 | case 1: 207 | def = args[0] 208 | default: 209 | log.Panicf("MustString() received too many arguments %d", len(args)) 210 | } 211 | 212 | s, err := j.String() 213 | if err == nil { 214 | return s 215 | } 216 | 217 | return def 218 | } 219 | 220 | // MustInt guarantees the return of an `int` (with optional default) 221 | // 222 | // useful when you explicitly want an `int` in a single value return context: 223 | // myFunc(js.Get("param1").MustInt(), js.Get("optional_param").MustInt(5150)) 224 | func (j *Json) MustInt(args ...int) int { 225 | var def int 226 | 227 | switch len(args) { 228 | case 0: 229 | break 230 | case 1: 231 | def = args[0] 232 | default: 233 | log.Panicf("MustInt() received too many arguments %d", len(args)) 234 | } 235 | 236 | i, err := j.Int() 237 | if err == nil { 238 | return i 239 | } 240 | 241 | return def 242 | } 243 | 244 | // MustBool guarantees the return of an `bool` (with optional default) 245 | // 246 | // useful when you explicitly want an `bool` in a single value return context: 247 | // myFunc(js.Get("param1").MustBool(), js.Get("optional_param").MustBool(false)) 248 | func (j *Json) MustBool(args ...bool) bool { 249 | var def bool 250 | 251 | switch len(args) { 252 | case 0: 253 | break 254 | case 1: 255 | def = args[0] 256 | default: 257 | log.Panicf("MustBool() received too many arguments %d", len(args)) 258 | } 259 | 260 | i, err := j.Bool() 261 | if err == nil { 262 | return i 263 | } 264 | 265 | return def 266 | } 267 | 268 | // MustFloat64 guarantees the return of a `float64` (with optional default) 269 | // 270 | // useful when you explicitly want a `float64` in a single value return context: 271 | // myFunc(js.Get("param1").MustFloat64(), js.Get("optional_param").MustFloat64(5.150)) 272 | func (j *Json) MustFloat64(args ...float64) float64 { 273 | var def float64 274 | 275 | switch len(args) { 276 | case 0: 277 | break 278 | case 1: 279 | def = args[0] 280 | default: 281 | log.Panicf("MustFloat64() received too many arguments %d", len(args)) 282 | } 283 | 284 | i, err := j.Float64() 285 | if err == nil { 286 | return i 287 | } 288 | 289 | return def 290 | } 291 | 292 | func (j *Json) MustArray(args ...[]interface{}) []interface{} { 293 | var def []interface{} 294 | 295 | switch len(args) { 296 | case 0: 297 | break 298 | case 1: 299 | def = args[0] 300 | default: 301 | log.Panicf("MustArray() received too many arguments %d", len(args)) 302 | } 303 | 304 | i, err := j.Array() 305 | if err == nil { 306 | return i 307 | } 308 | 309 | return def 310 | } 311 | -------------------------------------------------------------------------------- /webqq/tools/tools.go: -------------------------------------------------------------------------------- 1 | package tools 2 | 3 | import ( 4 | "fmt" 5 | "io" 6 | "io/ioutil" 7 | "os" 8 | "reflect" 9 | ) 10 | 11 | func init() { 12 | 13 | } 14 | 15 | func ReadString(re io.Reader) string { 16 | bBody, _ := ioutil.ReadAll(re) 17 | return string(bBody) 18 | } 19 | 20 | func ReadByte(re io.Reader) []byte { 21 | bBody, _ := ioutil.ReadAll(re) 22 | return bBody 23 | } 24 | 25 | func ErrHandle(err error, c string, des ...string) { 26 | if err != nil { 27 | switch c { 28 | case `p`: 29 | 30 | { 31 | for _, v := range des { 32 | fmt.Println(v) 33 | } 34 | panic(err) 35 | } 36 | case `x`: 37 | { 38 | for _, v := range des { 39 | fmt.Println(v) 40 | } 41 | fmt.Println(err) 42 | os.Exit(0) 43 | } 44 | case `n`: 45 | { 46 | for _, v := range des { 47 | fmt.Println(v) 48 | } 49 | fmt.Println(err) 50 | } 51 | } 52 | } 53 | } 54 | 55 | func PrintlnStruct(i interface{}) { 56 | t := reflect.TypeOf(i) 57 | for i := 0; i < t.NumField(); i++ { 58 | fmt.Println(t.Field(i).Name, t.Field(i).Type.String()) 59 | } 60 | 61 | } 62 | --------------------------------------------------------------------------------