添加抖音解析功能

This commit is contained in:
NothAmor
2023-07-17 18:11:18 +08:00
parent 8aaf8c3da6
commit 16d8f81ee2
9 changed files with 545 additions and 16 deletions

8
config.yaml Normal file
View File

@@ -0,0 +1,8 @@
DouYin:
protocol: http
address: 124.221.152.192
port: "5001"
router:
api: api
download: download
douyin_video_data: douyin_video_data

5
config/config.go Normal file
View File

@@ -0,0 +1,5 @@
package config
type Config struct {
DouYin DouYin `mapstructure:"DouYin" json:"DouYin" yaml:"DouYin"`
}

12
config/douyin.go Normal file
View File

@@ -0,0 +1,12 @@
package config
type DouYin struct {
Protocol string `yaml:"protocol"`
Address string `yaml:"address"`
Port string `yaml:"port"`
Router struct {
Api string `yaml:"api"`
Download string `yaml:"download"`
DouyinVideoData string `yaml:"douyin_video_data"`
} `yaml:"router"`
}

177
controller/douyin.go Normal file
View File

@@ -0,0 +1,177 @@
package controller
import (
"encoding/json"
"fmt"
"go-bot/bot/global"
"go-bot/bot/structs"
"io/ioutil"
"net/http"
"os"
"strings"
"github.com/eatmoreapple/openwechat"
)
// DouYinResolution 抖音解析
// Author: NothAmor
func DouYinResolution(msg *openwechat.Message) {
split := strings.Split(msg.Content, " ")
if len(split) == 1 {
msg.ReplyText("请输入正确的抖音链接")
return
}
// 获取抖音链接
douyinUrl := split[1]
// 获取抖音视频信息
data, err := GetDouYinUrlInfo(douyinUrl)
if err != nil {
msg.ReplyText(fmt.Sprintf("获取抖音信息失败, 失败原因: %s", err.Error()))
return
}
// 图片解析
if data.Type == "image" {
msg.ReplyText(fmt.Sprintf("解析类型: %s\n解析时长: %f\n图片ID: %s\n图片描述: %s\n作者昵称: %s\n作者ID: %s\n", data.Type, data.TotalTime, data.AwemeID, data.Desc, data.Author.Nickname, data.Author.UID))
for _, imageUrl := range data.ImageData.NoWatermarkImageList {
image, err := DownloadPic(imageUrl)
if err != nil {
msg.ReplyText(fmt.Sprintf("下载图片失败, 失败原因: %s", err.Error()))
return
}
msg.ReplyImage(image)
}
return
}
// 视频解析
if data.Type == "video" {
msg.ReplyText(fmt.Sprintf("解析类型: %s\n解析时长: %f\n视频ID: %s\n视频描述: %s\n作者昵称: %s\n作者ID: %s\n", data.Type, data.TotalTime, data.AwemeID, data.Desc, data.Author.Nickname, data.Author.UID))
video, err := DownloadVideo(data.VideoData.NwmVideoURLHQ)
if err != nil {
msg.ReplyText(fmt.Sprintf("下载视频失败, 失败原因: %s", err.Error()))
return
}
msg.ReplyVideo(video)
return
}
}
// GetDouYinUrlInfo 获取抖音链接信息
// Author: NothAmor
func GetDouYinUrlInfo(url string) (resolutionResponse structs.ResolutionResponse, err error) {
// 拼接GET链接请求API服务
resolutionApiUrl := fmt.Sprintf("%s://%s:%s/%s?url=%s", global.Config.DouYin.Protocol, global.Config.DouYin.Address, global.Config.DouYin.Port, global.Config.DouYin.Router.Api, url)
client := &http.Client{}
req, err := http.NewRequest("GET", resolutionApiUrl, nil)
if err != nil {
err = fmt.Errorf("请求链接错误: %s", err)
return
}
resp, err := client.Do(req)
if err != nil {
err = fmt.Errorf("请求链接错误: %s", err)
return
}
defer resp.Body.Close()
err = json.NewDecoder(resp.Body).Decode(&resolutionResponse)
if err != nil {
err = fmt.Errorf("解析返回数据错误: %s", err)
return
}
if resolutionResponse.Status != "success" {
err = fmt.Errorf("解析返回数据错误: %s", resolutionResponse.Message)
return
}
return
}
// DownloadPic 下载图片
// Author: NothAmor
func DownloadPic(url string) (image *os.File, err error) {
resp, err := http.Get(url)
if err != nil {
err = fmt.Errorf("请求链接错误: %s", err)
return
}
defer resp.Body.Close()
imageBytes, err := ioutil.ReadAll(resp.Body)
if err != nil {
err = fmt.Errorf("读取返回数据错误: %s", err)
return
}
tmpFile, err := ioutil.TempFile("", "image*.jpg")
if err != nil {
err = fmt.Errorf("创建临时文件错误: %s", err)
return
}
defer tmpFile.Close()
_, err = tmpFile.Write(imageBytes)
if err != nil {
err = fmt.Errorf("写入临时文件错误: %s", err)
return
}
_, err = tmpFile.Seek(0, 0)
if err != nil {
err = fmt.Errorf("设置临时文件指针错误: %s", err)
return
}
return tmpFile, nil
}
// DownloadVideo 下载视频
// Author: NothAmor
func DownloadVideo(url string) (video *os.File, err error) {
resp, err := http.Get(url)
if err != nil {
err = fmt.Errorf("请求链接错误: %s", err)
return
}
defer resp.Body.Close()
videoBytes, err := ioutil.ReadAll(resp.Body)
if err != nil {
err = fmt.Errorf("读取返回数据错误: %s", err)
return
}
tmpFile, err := ioutil.TempFile("", "video*.mp4")
if err != nil {
err = fmt.Errorf("创建临时文件错误: %s", err)
return
}
defer tmpFile.Close()
_, err = tmpFile.Write(videoBytes)
if err != nil {
err = fmt.Errorf("写入临时文件错误: %s", err)
return
}
_, err = tmpFile.Seek(0, 0)
if err != nil {
err = fmt.Errorf("设置临时文件指针错误: %s", err)
return
}
return tmpFile, nil
}

14
global/init.go Normal file
View File

@@ -0,0 +1,14 @@
package global
import (
"go-bot/bot/config"
"github.com/eatmoreapple/openwechat"
)
var (
// Bot 微信机器人
Bot *openwechat.Bot
// 配置信息
Config config.Config
)

8
go.mod
View File

@@ -3,11 +3,15 @@ module go-bot/bot
go 1.18
require (
github.com/eatmoreapple/openwechat v1.1.11
github.com/eatmoreapple/openwechat v1.4.3
github.com/go-sql-driver/mysql v1.6.0
github.com/kirinlabs/HttpRequest v1.1.1
github.com/robfig/cron/v3 v3.0.0
github.com/satori/go.uuid v1.2.0
)
require gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
require (
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

7
go.sum
View File

@@ -1,5 +1,7 @@
github.com/eatmoreapple/openwechat v1.1.11 h1:YJL8tUenK1NTflNPt5lWOl0KWcP0CTGjWNYAvN0dGFk=
github.com/eatmoreapple/openwechat v1.1.11/go.mod h1:61HOzTyvLobGdgWhL68jfGNwTJEv0mhQ1miCXQrvWU8=
github.com/eatmoreapple/openwechat v1.4.3 h1:hpqR3M0c180GN5e6sfkqdTmna1+vnvohqv8LkS7MecI=
github.com/eatmoreapple/openwechat v1.4.3/go.mod h1:ZxMcq7IpVWVU9JG7ERjExnm5M8/AQ6yZTtX30K3rwRQ=
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/kirinlabs/HttpRequest v1.1.1 h1:eBbFzpRd/Y7vQhRY30frHK3yAJiT1wDlB31Ryzyklc0=
@@ -13,5 +15,10 @@ github.com/robfig/cron/v3 v3.0.0 h1:kQ6Cb7aHOHTSzNVNEhmp8EcWKLb4CbiMW9h9VyIhO4E=
github.com/robfig/cron/v3 v3.0.0/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro=
github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

47
main.go
View File

@@ -2,31 +2,46 @@ package main
import (
"fmt"
"github.com/eatmoreapple/openwechat"
"go-bot/bot/config"
"go-bot/bot/controller"
"go-bot/bot/database"
"go-bot/bot/global"
"os"
"strings"
"github.com/eatmoreapple/openwechat"
"gopkg.in/yaml.v3"
)
func main() {
// 创建bot 对象
bot := openwechat.DefaultBot(openwechat.Desktop)
// 创建热存储容器对象
reloadStorage := openwechat.NewJsonFileHotReloadStorage("storage.json")
//bot.HotLogin(reloadStorage)
// 执行热登录
err := bot.HotLogin(reloadStorage, true)
// 初始化配置文件
dataBytes, err := os.ReadFile("config.yaml")
if err != nil {
fmt.Println(err)
fmt.Printf("读取配置文件信息出错: %s\n", err)
return
}
global.Config = config.Config{}
err = yaml.Unmarshal(dataBytes, &global.Config)
if err != nil {
fmt.Printf("解析配置文件信息出错: %s\n", err)
return
}
// 创建bot 对象
global.Bot = openwechat.DefaultBot(openwechat.Desktop)
// 创建热存储容器对象
reloadStorage := openwechat.NewFileHotReloadStorage("storage.json")
defer reloadStorage.Close()
// 执行热登录
global.Bot.HotLogin(reloadStorage)
// 注册登陆二维码回调
bot.UUIDCallback = openwechat.PrintlnQrcodeUrl
global.Bot.UUIDCallback = openwechat.PrintlnQrcodeUrl
// 获取登陆的用户
self, err := bot.GetCurrentUser()
self, err := global.Bot.GetCurrentUser()
if err != nil {
fmt.Println(self, err)
return
@@ -44,7 +59,7 @@ func main() {
database.Database()
// 注册消息处理函数
bot.MessageHandler = func(msg *openwechat.Message) {
global.Bot.MessageHandler = func(msg *openwechat.Message) {
if msg.IsText() {
split := strings.Split(msg.Content, " ")
@@ -68,6 +83,10 @@ func main() {
controller.GetWeather(msg, self)
}
if split[0] == "抖音" || split[0] == "dy" {
controller.DouYinResolution(msg)
}
if split[0] == "school" && split[1] == "start" {
controller.SchoolSignInBegin(msg, self)
}
@@ -87,5 +106,5 @@ func main() {
}
// 阻塞主goroutine, 直到发生异常或者用户主动退出
bot.Block()
global.Bot.Block()
}

283
structs/douyin.go Normal file
View File

@@ -0,0 +1,283 @@
package structs
type ResolutionResponse struct {
URL string `json:"url"`
Endpoint string `json:"endpoint"`
TotalTime float64 `json:"total_time"`
Status string `json:"status"`
Message string `json:"message"`
Type string `json:"type"`
Platform string `json:"platform"`
AwemeID string `json:"aweme_id"`
OfficialAPIURL struct {
UserAgent string `json:"User-Agent"`
APIURL string `json:"api_url"`
} `json:"official_api_url"`
Desc string `json:"desc"`
CreateTime int `json:"create_time"`
Author struct {
AvatarThumb struct {
Height int `json:"height"`
URI string `json:"uri"`
URLList []string `json:"url_list"`
Width int `json:"width"`
} `json:"avatar_thumb"`
CfList any `json:"cf_list"`
CloseFriendType int `json:"close_friend_type"`
ContactsStatus int `json:"contacts_status"`
ContrailList any `json:"contrail_list"`
CoverURL []struct {
Height int `json:"height"`
URI string `json:"uri"`
URLList []string `json:"url_list"`
Width int `json:"width"`
} `json:"cover_url"`
CreateTime int `json:"create_time"`
CustomVerify string `json:"custom_verify"`
DataLabelList any `json:"data_label_list"`
EndorsementInfoList any `json:"endorsement_info_list"`
EnterpriseVerifyReason string `json:"enterprise_verify_reason"`
FamiliarVisitorUser any `json:"familiar_visitor_user"`
FavoritingCount int `json:"favoriting_count"`
FollowStatus int `json:"follow_status"`
FollowStatusErrCode int `json:"follow_status_err_code"`
FollowerCount int `json:"follower_count"`
FollowerListSecondaryInformationStruct any `json:"follower_list_secondary_information_struct"`
FollowerStatus int `json:"follower_status"`
FollowingCount int `json:"following_count"`
ImRoleIds any `json:"im_role_ids"`
IsAdFake bool `json:"is_ad_fake"`
IsBan bool `json:"is_ban"`
IsBlockedV2 bool `json:"is_blocked_v2"`
IsBlockingV2 bool `json:"is_blocking_v2"`
IsCf int `json:"is_cf"`
LiveHighValue int `json:"live_high_value"`
MaxFollowerCount int `json:"max_follower_count"`
Nickname string `json:"nickname"`
NotSeenItemIDList any `json:"not_seen_item_id_list"`
NotSeenItemIDListV2 any `json:"not_seen_item_id_list_v2"`
OfflineInfoList any `json:"offline_info_list"`
PersonalTagList any `json:"personal_tag_list"`
PreventDownload bool `json:"prevent_download"`
RiskNoticeText string `json:"risk_notice_text"`
SecUID string `json:"sec_uid"`
Secret int `json:"secret"`
ShareInfo struct {
ShareDesc string `json:"share_desc"`
ShareDescInfo string `json:"share_desc_info"`
ShareQrcodeURL struct {
Height int `json:"height"`
URI string `json:"uri"`
URLList []string `json:"url_list"`
Width int `json:"width"`
} `json:"share_qrcode_url"`
ShareTitle string `json:"share_title"`
ShareTitleMyself string `json:"share_title_myself"`
ShareTitleOther string `json:"share_title_other"`
ShareURL string `json:"share_url"`
ShareWeiboDesc string `json:"share_weibo_desc"`
} `json:"share_info"`
ShortID string `json:"short_id"`
Signature string `json:"signature"`
SignatureExtra any `json:"signature_extra"`
SpecialFollowStatus int `json:"special_follow_status"`
SpecialPeopleLabels any `json:"special_people_labels"`
Status int `json:"status"`
TextExtra any `json:"text_extra"`
TotalFavorited int `json:"total_favorited"`
UID string `json:"uid"`
UniqueID string `json:"unique_id"`
UserAge int `json:"user_age"`
UserCanceled bool `json:"user_canceled"`
UserPermissions any `json:"user_permissions"`
VerificationType int `json:"verification_type"`
} `json:"author"`
Music struct {
Album string `json:"album"`
ArtistUserInfos any `json:"artist_user_infos"`
Artists []any `json:"artists"`
AuditionDuration int `json:"audition_duration"`
Author string `json:"author"`
AuthorDeleted bool `json:"author_deleted"`
AuthorPosition any `json:"author_position"`
AuthorStatus int `json:"author_status"`
AvatarLarge struct {
Height int `json:"height"`
URI string `json:"uri"`
URLList []string `json:"url_list"`
Width int `json:"width"`
} `json:"avatar_large"`
AvatarMedium struct {
Height int `json:"height"`
URI string `json:"uri"`
URLList []string `json:"url_list"`
Width int `json:"width"`
} `json:"avatar_medium"`
AvatarThumb struct {
Height int `json:"height"`
URI string `json:"uri"`
URLList []string `json:"url_list"`
Width int `json:"width"`
} `json:"avatar_thumb"`
BindedChallengeID int `json:"binded_challenge_id"`
CanBackgroundPlay bool `json:"can_background_play"`
CollectStat int `json:"collect_stat"`
CoverHd struct {
Height int `json:"height"`
URI string `json:"uri"`
URLList []string `json:"url_list"`
Width int `json:"width"`
} `json:"cover_hd"`
CoverLarge struct {
Height int `json:"height"`
URI string `json:"uri"`
URLList []string `json:"url_list"`
Width int `json:"width"`
} `json:"cover_large"`
CoverMedium struct {
Height int `json:"height"`
URI string `json:"uri"`
URLList []string `json:"url_list"`
Width int `json:"width"`
} `json:"cover_medium"`
CoverThumb struct {
Height int `json:"height"`
URI string `json:"uri"`
URLList []string `json:"url_list"`
Width int `json:"width"`
} `json:"cover_thumb"`
DmvAutoShow bool `json:"dmv_auto_show"`
DspStatus int `json:"dsp_status"`
Duration int `json:"duration"`
EndTime int `json:"end_time"`
ExternalSongInfo []any `json:"external_song_info"`
Extra string `json:"extra"`
ID int64 `json:"id"`
IDStr string `json:"id_str"`
IsAudioURLWithCookie bool `json:"is_audio_url_with_cookie"`
IsCommerceMusic bool `json:"is_commerce_music"`
IsDelVideo bool `json:"is_del_video"`
IsMatchedMetadata bool `json:"is_matched_metadata"`
IsOriginal bool `json:"is_original"`
IsOriginalSound bool `json:"is_original_sound"`
IsPgc bool `json:"is_pgc"`
IsRestricted bool `json:"is_restricted"`
IsVideoSelfSee bool `json:"is_video_self_see"`
LunaInfo struct {
IsLunaUser bool `json:"is_luna_user"`
HasCopyright bool `json:"has_copyright"`
} `json:"luna_info"`
LyricShortPosition any `json:"lyric_short_position"`
MatchedPgcSound struct {
Author string `json:"author"`
CoverMedium struct {
Height int `json:"height"`
URI string `json:"uri"`
URLList []string `json:"url_list"`
Width int `json:"width"`
} `json:"cover_medium"`
MixedAuthor string `json:"mixed_author"`
MixedTitle string `json:"mixed_title"`
Title string `json:"title"`
} `json:"matched_pgc_sound"`
Mid string `json:"mid"`
MusicChartRanks any `json:"music_chart_ranks"`
MusicCollectCount int `json:"music_collect_count"`
MusicCoverAtmosphereColorValue string `json:"music_cover_atmosphere_color_value"`
MusicStatus int `json:"music_status"`
MusicianUserInfos any `json:"musician_user_infos"`
MuteShare bool `json:"mute_share"`
OfflineDesc string `json:"offline_desc"`
OwnerHandle string `json:"owner_handle"`
OwnerID string `json:"owner_id"`
OwnerNickname string `json:"owner_nickname"`
PgcMusicType int `json:"pgc_music_type"`
PlayURL struct {
Height int `json:"height"`
URI string `json:"uri"`
URLKey string `json:"url_key"`
URLList []string `json:"url_list"`
Width int `json:"width"`
} `json:"play_url"`
Position any `json:"position"`
PreventDownload bool `json:"prevent_download"`
PreventItemDownloadStatus int `json:"prevent_item_download_status"`
PreviewEndTime int `json:"preview_end_time"`
PreviewStartTime int `json:"preview_start_time"`
ReasonType int `json:"reason_type"`
Redirect bool `json:"redirect"`
SchemaURL string `json:"schema_url"`
SearchImpr struct {
EntityID string `json:"entity_id"`
} `json:"search_impr"`
SecUID string `json:"sec_uid"`
ShootDuration int `json:"shoot_duration"`
Song struct {
Artists any `json:"artists"`
ChorusV3Infos any `json:"chorus_v3_infos"`
ID int64 `json:"id"`
IDStr string `json:"id_str"`
} `json:"song"`
SourcePlatform int `json:"source_platform"`
StartTime int `json:"start_time"`
Status int `json:"status"`
StrongBeatURL struct {
Height int `json:"height"`
URI string `json:"uri"`
URLList []string `json:"url_list"`
Width int `json:"width"`
} `json:"strong_beat_url"`
TagList any `json:"tag_list"`
Title string `json:"title"`
UnshelveCountries any `json:"unshelve_countries"`
UserCount int `json:"user_count"`
VideoDuration int `json:"video_duration"`
} `json:"music"`
Statistics struct {
AdmireCount int `json:"admire_count"`
AwemeID string `json:"aweme_id"`
CollectCount int `json:"collect_count"`
CommentCount int `json:"comment_count"`
DiggCount int `json:"digg_count"`
PlayCount int `json:"play_count"`
ShareCount int `json:"share_count"`
} `json:"statistics"`
CoverData struct {
Cover struct {
Height int `json:"height"`
URI string `json:"uri"`
URLList []string `json:"url_list"`
Width int `json:"width"`
} `json:"cover"`
OriginCover struct {
Height int `json:"height"`
URI string `json:"uri"`
URLList []string `json:"url_list"`
Width int `json:"width"`
} `json:"origin_cover"`
DynamicCover struct {
Height int `json:"height"`
URI string `json:"uri"`
URLList []string `json:"url_list"`
Width int `json:"width"`
} `json:"dynamic_cover"`
} `json:"cover_data"`
Hashtags []struct {
End int `json:"end"`
HashtagID string `json:"hashtag_id"`
HashtagName string `json:"hashtag_name"`
IsCommerce bool `json:"is_commerce"`
Start int `json:"start"`
Type int `json:"type"`
} `json:"hashtags"`
VideoData struct {
WmVideoURL string `json:"wm_video_url"`
WmVideoURLHQ string `json:"wm_video_url_HQ"`
NwmVideoURL string `json:"nwm_video_url"`
NwmVideoURLHQ string `json:"nwm_video_url_HQ"`
} `json:"video_data"`
ImageData struct {
NoWatermarkImageList []string `json:"no_watermark_image_list"`
WatermarkImageList []string `json:"watermark_image_list"`
} `json:"image_data"`
}