add youtube webhook

This commit is contained in:
Jay 2018-09-15 23:09:41 +08:00
parent e954afe80b
commit 16190c49c4
7 changed files with 350 additions and 27 deletions

View File

@ -34,11 +34,11 @@ func GetFacebookPage(id string) (page *FacebookPage, err error) {
// AddPage - // AddPage -
func (p *FacebookPage) AddPage() (err error) { func (p *FacebookPage) AddPage() (err error) {
rows, err := x.NamedQuery(`insert into "public"."facebook_page" ("id", "lastpost") values (:id, :lastpost) returning *`, p) stmt, err := x.PrepareNamed(`insert into "public"."facebook_page" ("id", "lastpost") values (:id, :lastpost) returning *`)
if err != nil { if err != nil {
return err return err
} }
err = rows.StructScan(&p) err = stmt.Get(p, p)
return return
} }

View File

@ -25,14 +25,42 @@ func (p *LineFacebookRT) AddRT() (err error) {
return return
} }
// DelRT -
func (p *LineFacebookRT) DelRT() (err error) {
_, err = x.NamedExec(`delete from "public"."line_fb_rt" where "line" = :line and "facebook" = :facebook`, p)
return
}
// GetRT -
func (p *LineFacebookRT) GetRT() (err error) {
stmt, err := x.PrepareNamed(`select * from "public"."line_fb_rt" where "line" = :line and "facebook" = :facebook`)
if err != nil {
return err
}
err = stmt.Get(p, p)
return
}
// AddRT - add twitch line rt // AddRT - add twitch line rt
func (p *LineTwitchRT) AddRT() (err error) { func (p *LineTwitchRT) AddRT() (err error) {
_, err = x.NamedExec(`insert into "public"."line_twitch_rt" ("line", "twitch", "type", "tmpl") values (:line, :twitch, :type, :tmpl)`, p) _, err = x.NamedExec(`insert into "public"."line_twitch_rt" ("line", "twitch", "type", "tmpl") values (:line, :twitch, :type, :tmpl)`, p)
return return
} }
// DelRT -
func (p *LineTwitchRT) DelRT() (err error) {
_, err = x.NamedExec(`delete from "public"."line_twitch_rt" where "line" = :line and "twitch" = :twitch and "type" = :type`, p)
return
}
// AddRT - add youtube line rt // AddRT - add youtube line rt
func (p *LineYoutubeRT) AddRT() (err error) { func (p *LineYoutubeRT) AddRT() (err error) {
_, err = x.NamedExec(`insert into "public"."line_youtube_rt" ("line", "youtube", "tmpl") values (:line, :youtube, :tmpl)`, p) _, err = x.NamedExec(`insert into "public"."line_youtube_rt" ("line", "youtube", "tmpl") values (:line, :youtube, :tmpl)`, p)
return return
} }
// DelRT -
func (p *LineYoutubeRT) DelRT() (err error) {
_, err = x.NamedExec(`delete from "public"."line_youtube_rt" where "line" = :line and "youtube" = :youtube`, p)
return
}

View File

@ -32,13 +32,24 @@ func GetJoinChatChannel() (channels []*TwitchChannel, err error) {
return return
} }
// Add - // GetWithName -
func (p *TwitchChannel) Add() (err error) { func (p *TwitchChannel) GetWithName() (err error) {
rows, err := x.NamedQuery(`insert into "public"."twitch_channel" ("name", "laststream", "join", "opayid") values (:name, :laststream, :join, :opayid) returning *`, p) stmt, err := x.PrepareNamed(`select * from "public"."twitch_channel" where "name" = :name`)
if err != nil { if err != nil {
return err return err
} }
err = rows.StructScan(p)
err = stmt.Get(p, p)
return
}
// Add -
func (p *TwitchChannel) Add() (err error) {
stmt, err := x.PrepareNamed(`insert into "public"."twitch_channel" ("name", "laststream", "join", "opayid") values (:name, :laststream, :join, :opayid) returning *`)
if err != nil {
return err
}
err = stmt.Get(p, p)
return return
} }

View File

@ -2,12 +2,55 @@ package model
import "time" import "time"
// YoutubeGroup -
type YoutubeGroup struct {
*LineGroup
Tmpl string `db:"tmpl"`
}
// YoutubeChannel - // YoutubeChannel -
type YoutubeChannel struct { type YoutubeChannel struct {
ID string `db:"id" cc:"id"` ID string `db:"id" cc:"id"`
Name string `db:"name" cc:"name"` Name string `db:"name" cc:"name"`
LastVideo string `db:"lastvideo" cc:"lastvideo"` LastVideo string `db:"lastvideo" cc:"lastvideo"`
Expire int32 `db:"expire" cc:"expire"` Expire int64 `db:"expire" cc:"expire"`
Ctime time.Time `db:"ctime" cc:"ctime"` Ctime time.Time `db:"ctime" cc:"ctime"`
Mtime time.Time `db:"mtime" cc:"mtime"` Mtime time.Time `db:"mtime" cc:"mtime"`
Groups []*YoutubeGroup `db:"-"`
}
// GetYoutubeChannelWithID -
func GetYoutubeChannelWithID(id string) (yt *YoutubeChannel, err error) {
err = x.Get(&yt, `select * from "public"."youtube_channel" where "id" = $1`, id)
return
}
// UpdateLastVideo -
func (p *YoutubeChannel) UpdateLastVideo(vid string) (err error) {
p.LastVideo = vid
_, err = x.NamedExec(`update "public"."youtube_channel" set "lastvideo" = :lastvideo where "id" = :id`, p)
return
}
// UpdateExpire -
func (p *YoutubeChannel) UpdateExpire(t int64) (err error) {
p.Expire = t
_, err = x.NamedExec(`update "public"."youtube_channel" set "expire" = :expire where "id" = :id`, p)
if err != nil {
return err
}
return
}
// GetGroups -
func (p *YoutubeChannel) GetGroups() (err error) {
query := `select g.*, rt.tmpl as tmpl from "public"."youtube_channel" yt
left join "public"."line_youtube_rt" rt
on rt.youtube = yt.id
left join "public"."line_group" g
on g.id = rt.line
where
yt.id = $1`
err = x.Select(&p.Groups, query, p.ID)
return
} }

View File

@ -18,6 +18,10 @@ func selectAct(cmd, sub, txt string, s *lineobj.SourceObject) (res string) {
return addFacebookPage(sub, txt, s) return addFacebookPage(sub, txt, s)
case "addtwitch": case "addtwitch":
return addTwitchChannel(sub, txt, s) return addTwitchChannel(sub, txt, s)
case "delpage":
return delFacebookPage(sub, txt, s)
case "deltwitch":
return delTwitchChannel(sub, txt, s)
} }
return return
} }
@ -48,18 +52,29 @@ func addLineGroup(sub, txt string, s *lineobj.SourceObject) (res string) {
return "Success" return "Success"
} }
func addFacebookPage(sub, txt string, s *lineobj.SourceObject) (res string) { func checkGroupOwner(s *lineobj.SourceObject) (ok bool, err error) {
// args = pageid tmpl
exists, err := model.CheckGroup(s.GroupID) exists, err := model.CheckGroup(s.GroupID)
if err != nil { if err != nil {
return "run check group error" return false, err
} }
if !exists { if !exists {
return "group not exists" return false, nil
} }
ok, err := model.CheckGroupOwner(s.UserID, s.GroupID) ok, err = model.CheckGroupOwner(s.UserID, s.GroupID)
if err != nil { if err != nil {
return "run check group owner fail" return false, err
}
if !ok {
return false, nil
}
return true, nil
}
func addFacebookPage(sub, txt string, s *lineobj.SourceObject) (res string) {
// args = pageid tmpl
ok, err := checkGroupOwner(s)
if err != nil {
return "check group fail"
} }
if !ok { if !ok {
return "not owner" return "not owner"
@ -98,18 +113,47 @@ func addFacebookPage(sub, txt string, s *lineobj.SourceObject) (res string) {
return "Success" return "Success"
} }
func delFacebookPage(sub, txt string, s *lineobj.SourceObject) (res string) {
// args = pageid
ok, err := checkGroupOwner(s)
if err != nil {
return "check group fail"
}
if !ok {
return "not owner"
}
args := strings.Split(strings.Trim(txt, " "), " ")
if len(args) < 1 {
return "commage arg not match"
}
rt := &model.LineFacebookRT{
Line: s.GroupID,
Facebook: args[0],
}
err = rt.DelRT()
if err != nil {
return "remove facebook page fail"
}
return "Success"
}
func checkTwitchType(t string) bool {
switch t {
case "live":
default:
return false
}
return true
}
func addTwitchChannel(sub, txt string, s *lineobj.SourceObject) (res string) { func addTwitchChannel(sub, txt string, s *lineobj.SourceObject) (res string) {
// args = twitchLogin type tmpl // args = twitchLogin type tmpl
exists, err := model.CheckGroup(s.GroupID) ok, err := checkGroupOwner(s)
if err != nil { if err != nil {
return "run check group error" return "check group fail"
}
if !exists {
return "group not exists"
}
ok, err := model.CheckGroupOwner(s.UserID, s.GroupID)
if err != nil {
return "run check group owner fail"
} }
if !ok { if !ok {
return "not owner" return "not owner"
@ -120,6 +164,10 @@ func addTwitchChannel(sub, txt string, s *lineobj.SourceObject) (res string) {
return "command args not match" return "command args not match"
} }
if !checkTwitchType(args[1]) {
return "type not allow"
}
info := twitch.GetUserDataByName(args[0]) info := twitch.GetUserDataByName(args[0])
if info == nil { if info == nil {
return "get twitch user id fail" return "get twitch user id fail"
@ -137,6 +185,7 @@ func addTwitchChannel(sub, txt string, s *lineobj.SourceObject) (res string) {
rt := &model.LineTwitchRT{ rt := &model.LineTwitchRT{
Line: s.GroupID, Line: s.GroupID,
Twitch: info.ID, Twitch: info.ID,
Type: args[1],
Tmpl: strings.Join(args[2:], " "), Tmpl: strings.Join(args[2:], " "),
} }
err = rt.AddRT() err = rt.AddRT()
@ -146,3 +195,47 @@ func addTwitchChannel(sub, txt string, s *lineobj.SourceObject) (res string) {
return "Success" return "Success"
} }
func delTwitchChannel(sub, txt string, s *lineobj.SourceObject) (res string) {
// args = twitchLogin type
ok, err := checkGroupOwner(s)
if err != nil {
return "check group fail"
}
if !ok {
return "not owner"
}
args := strings.Split(strings.Trim(txt, " "), " ")
if len(args) < 2 {
return "command arg not match"
}
if !checkTwitchType(args[1]) {
return "type not allow"
}
ch := &model.TwitchChannel{
Name: args[0],
}
err = ch.GetWithName()
if err != nil {
return "get channel data fail"
}
if ch == nil {
return "Success"
}
rt := &model.LineTwitchRT{
Line: s.GroupID,
Twitch: ch.ID,
Type: args[1],
}
err = rt.DelRT()
if err != nil {
return "delete rt fail"
}
return "Success"
}

135
router/google/youtube.go Normal file
View File

@ -0,0 +1,135 @@
package google
import (
"encoding/xml"
"fmt"
"io/ioutil"
"strings"
"time"
"git.trj.tw/golang/mtfosbot/model"
lineapi "git.trj.tw/golang/mtfosbot/module/apis/line"
"git.trj.tw/golang/mtfosbot/module/context"
)
type feed struct {
XMLName xml.Name `xml:"feed"`
Entry []entry `xml:"entry"`
}
type entry struct {
XMLName xml.Name `xml:"entry"`
Title string `xml:"title"`
ID string `xml:"id"`
Link link `xml:"link"`
Author []author `xml:"author"`
}
type link struct {
XMLName xml.Name `xml:"link"`
Href string `xml:"href,attr"`
}
type author struct {
XMLName xml.Name `xml:"author"`
Name string `xml:"name"`
URI string `xml:"uri"`
}
// VerifyWebhook -
func VerifyWebhook(c *context.Context) {
hubMode, ok := c.GetQuery("hub.mode")
if !ok {
c.DataFormat(nil)
return
}
challenge, ok := c.GetQuery("hub.challenge")
if !ok {
c.DataFormat(nil)
return
}
id, ok := c.GetQuery("id")
if !ok {
c.DataFormat(nil)
return
}
if hubMode == "subscribe" {
t := time.Now().Unix() + 86400
yt, err := model.GetYoutubeChannelWithID(id)
if err != nil {
c.ServerError(nil)
return
}
if yt == nil {
c.NotFound("channel not found")
return
}
err = yt.UpdateExpire(t)
if err != nil {
c.ServerError(nil)
}
}
c.String(200, challenge)
}
// GetNotifyWebhook -
func GetNotifyWebhook(c *context.Context) {
byteBody, err := ioutil.ReadAll(c.Request.Body)
defer c.Request.Body.Close()
if err != nil {
c.DataFormat(nil)
return
}
id, ok := c.GetQuery("id")
if !ok {
c.DataFormat(nil)
return
}
hook := &feed{}
err = xml.Unmarshal(byteBody, &hook)
if err != nil {
c.DataFormat(nil)
return
}
if len(hook.Entry) == 0 {
c.Success(nil)
return
}
yt, err := model.GetYoutubeChannelWithID(id)
if err != nil || yt == nil {
c.ServerError(nil)
return
}
if hook.Entry[0].ID == yt.LastVideo {
c.Success(nil)
return
}
err = yt.UpdateLastVideo(hook.Entry[0].ID)
if err != nil {
c.ServerError(nil)
return
}
for _, v := range yt.Groups {
if v.Notify {
str := v.Tmpl
if len(str) == 0 {
str = fmt.Sprintf("%s\n%s", hook.Entry[0].Title, hook.Entry[0].Link.Href)
} else {
str = strings.Replace(str, "{link}", hook.Entry[0].Link.Href, -1)
str = strings.Replace(str, "{txt}", hook.Entry[0].Title, -1)
}
msg := &lineapi.TextMessage{
Text: str,
}
lineapi.PushMessage(v.ID, msg)
}
}
c.Success(nil)
}

View File

@ -5,6 +5,8 @@ import (
"git.trj.tw/golang/mtfosbot/module/context" "git.trj.tw/golang/mtfosbot/module/context"
"git.trj.tw/golang/mtfosbot/router/api" "git.trj.tw/golang/mtfosbot/router/api"
"git.trj.tw/golang/mtfosbot/router/google"
"git.trj.tw/golang/mtfosbot/router/line"
"github.com/gin-contrib/cors" "github.com/gin-contrib/cors"
"github.com/gin-gonic/contrib/sessions" "github.com/gin-gonic/contrib/sessions"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
@ -43,4 +45,15 @@ func SetRoutes(r *gin.Engine) {
{ {
apiGroup.POST("/login", context.PatchCtx(api.UserLogin)) apiGroup.POST("/login", context.PatchCtx(api.UserLogin))
} }
lineApis := r.Group("/line")
{
lineApis.POST("/", context.PatchCtx(line.GetRawBody), context.PatchCtx(line.VerifyLine), context.PatchCtx(line.GetLineMessage))
}
googleApis := r.Group("/google")
{
googleApis.GET("/youtube/webhook", context.PatchCtx(google.VerifyWebhook))
googleApis.POST("/youtube/webhook", context.PatchCtx(google.GetNotifyWebhook))
}
} }