package main import ( "encoding/json" "fmt" "github.com/duke-git/lancet/v2/strutil" "github.com/golang-cz/devslog" "github.com/gotify/go-api-client/v2/auth" "github.com/gotify/go-api-client/v2/client/message" "github.com/gotify/go-api-client/v2/gotify" "github.com/gotify/go-api-client/v2/models" "github.com/sethvargo/go-githubactions" "github.com/spf13/cast" "log" "log/slog" "net/http" "net/url" "os" "strings" "time" ) var ( logger *slog.Logger ) func init() { slogOpts := &slog.HandlerOptions{ AddSource: false, Level: slog.LevelDebug, } opts := &devslog.Options{ HandlerOptions: slogOpts, MaxSlicePrintSize: 5, SortKeys: true, TimeFormat: "[15:04:05]", StringerFormatter: true, } logger = slog.New(devslog.NewHandler(os.Stdout, opts)) //slog.SetDefault(logger) } func main() { if err := doPush(); err != nil { logger.Error("推送失败", slog.Any("err", err)) log.Fatalln(err) } logger.Info("推送成功") githubactions.SetOutput("time", time.Now().Format("2006-01-02 15:04:05")) } func doPush() error { ctx, err := githubactions.Context() if err != nil { logger.Error("获取上下文失败", slog.Any("err", err)) return err } priority := githubactions.GetInput("priority") title := githubactions.GetInput("title") token := githubactions.GetInput("token") contentType := githubactions.GetInput("contentType") clickUrl := githubactions.GetInput("clickUrl") bigImageUrl := githubactions.GetInput("bigImageUrl") msgText := githubactions.GetInput("msgText") stringURL := githubactions.GetInput("url") msgText += "\n\n" + getExtraMsg(ctx) msg := &models.MessageExternal{ Message: msgText, Title: title, Priority: cast.ToInt(priority), } msg.Extras = map[string]interface{}{} if contentType != "" { msg.Extras["client::display"] = map[string]interface{}{ "contentType": contentType, } } clientNotification := map[string]interface{}{} if clickUrl != "" { clientNotification["click"] = map[string]string{"url": clickUrl} } if bigImageUrl != "" { clientNotification["bigImageUrl"] = bigImageUrl } if len(clientNotification) > 0 { msg.Extras["client::notification"] = clientNotification } parsedURL, err := url.Parse(stringURL) if err != nil { return err } return pushMessage(parsedURL, token, msg) } func pushMessage(parsedURL *url.URL, token string, msg *models.MessageExternal) error { client := gotify.NewClient(parsedURL, &http.Client{}) params := message.NewCreateMessageParams() params.SetBody(msg) res, err := client.Message.CreateMessage(params, auth.TokenAuth(token)) if err == nil { logger.Info("推送结果", slog.Any("result", res)) return nil } else { return err } } func getExtraMsg(ctx *githubactions.GitHubContext) (result string) { body, err := os.ReadFile(ctx.EventPath) if err != nil { logger.Error("读取文件失败", slog.Any("err", err), slog.String("path", ctx.EventPath)) return } event := new(Event) if err = json.Unmarshal(body, &event); err != nil { logger.Error("解析json失败", slog.Any("err", err), slog.String("path", ctx.EventPath), slog.String("body", string(body))) return } result = "**更新内容:**\n%s\n\n[查看更新](%s)\t[查看Action](%s)" var commits []string for _, v := range event.Commits { commits = append(commits, fmt.Sprintf("- %s [%s](%s)", v.Message, strutil.Substring(v.Id, 0, 6), v.Url)) } action := fmt.Sprintf("%s/%s/actions/runs/%d", ctx.ServerURL, ctx.Repository, ctx.RunNumber) result = fmt.Sprintf(result, strings.Join(commits, "\n"), event.CompareUrl, action) return } type Event struct { After string `json:"after"` Before string `json:"before"` Commits []struct { Added []interface{} `json:"added"` Author struct { Email string `json:"email"` Name string `json:"name"` Username string `json:"username"` } `json:"author"` Committer struct { Email string `json:"email"` Name string `json:"name"` Username string `json:"username"` } `json:"committer"` Id string `json:"id"` Message string `json:"message"` Modified []string `json:"modified"` Removed []interface{} `json:"removed"` Timestamp time.Time `json:"timestamp"` Url string `json:"url"` Verification interface{} `json:"verification"` } `json:"commits"` CompareUrl string `json:"compare_url"` HeadCommit struct { Added []interface{} `json:"added"` Author struct { Email string `json:"email"` Name string `json:"name"` Username string `json:"username"` } `json:"author"` Committer struct { Email string `json:"email"` Name string `json:"name"` Username string `json:"username"` } `json:"committer"` Id string `json:"id"` Message string `json:"message"` Modified []string `json:"modified"` Removed []interface{} `json:"removed"` Timestamp time.Time `json:"timestamp"` Url string `json:"url"` Verification interface{} `json:"verification"` } `json:"head_commit"` Pusher struct { Active bool `json:"active"` AvatarUrl string `json:"avatar_url"` Created time.Time `json:"created"` Description string `json:"description"` Email string `json:"email"` FollowersCount int `json:"followers_count"` FollowingCount int `json:"following_count"` FullName string `json:"full_name"` HtmlUrl string `json:"html_url"` Id int `json:"id"` IsAdmin bool `json:"is_admin"` Language string `json:"language"` LastLogin time.Time `json:"last_login"` Location string `json:"location"` Login string `json:"login"` LoginName string `json:"login_name"` ProhibitLogin bool `json:"prohibit_login"` Restricted bool `json:"restricted"` SourceId int `json:"source_id"` StarredReposCount int `json:"starred_repos_count"` Username string `json:"username"` Visibility string `json:"visibility"` Website string `json:"website"` } `json:"pusher"` Ref string `json:"ref"` Repository struct { AllowFastForwardOnlyMerge bool `json:"allow_fast_forward_only_merge"` AllowMergeCommits bool `json:"allow_merge_commits"` AllowRebase bool `json:"allow_rebase"` AllowRebaseExplicit bool `json:"allow_rebase_explicit"` AllowRebaseUpdate bool `json:"allow_rebase_update"` AllowSquashMerge bool `json:"allow_squash_merge"` Archived bool `json:"archived"` ArchivedAt time.Time `json:"archived_at"` AvatarUrl string `json:"avatar_url"` CloneUrl string `json:"clone_url"` CreatedAt time.Time `json:"created_at"` DefaultAllowMaintainerEdit bool `json:"default_allow_maintainer_edit"` DefaultBranch string `json:"default_branch"` DefaultDeleteBranchAfterMerge bool `json:"default_delete_branch_after_merge"` DefaultMergeStyle string `json:"default_merge_style"` Description string `json:"description"` Empty bool `json:"empty"` Fork bool `json:"fork"` ForksCount int `json:"forks_count"` FullName string `json:"full_name"` HasActions bool `json:"has_actions"` HasIssues bool `json:"has_issues"` HasPackages bool `json:"has_packages"` HasProjects bool `json:"has_projects"` HasPullRequests bool `json:"has_pull_requests"` HasReleases bool `json:"has_releases"` HasWiki bool `json:"has_wiki"` HtmlUrl string `json:"html_url"` Id int `json:"id"` IgnoreWhitespaceConflicts bool `json:"ignore_whitespace_conflicts"` Internal bool `json:"internal"` InternalTracker struct { AllowOnlyContributorsToTrackTime bool `json:"allow_only_contributors_to_track_time"` EnableIssueDependencies bool `json:"enable_issue_dependencies"` EnableTimeTracker bool `json:"enable_time_tracker"` } `json:"internal_tracker"` Language string `json:"language"` LanguagesUrl string `json:"languages_url"` Link string `json:"link"` Mirror bool `json:"mirror"` MirrorInterval string `json:"mirror_interval"` MirrorUpdated time.Time `json:"mirror_updated"` Name string `json:"name"` ObjectFormatName string `json:"object_format_name"` OpenIssuesCount int `json:"open_issues_count"` OpenPrCounter int `json:"open_pr_counter"` OriginalUrl string `json:"original_url"` Owner struct { Active bool `json:"active"` AvatarUrl string `json:"avatar_url"` Created time.Time `json:"created"` Description string `json:"description"` Email string `json:"email"` FollowersCount int `json:"followers_count"` FollowingCount int `json:"following_count"` FullName string `json:"full_name"` HtmlUrl string `json:"html_url"` Id int `json:"id"` IsAdmin bool `json:"is_admin"` Language string `json:"language"` LastLogin time.Time `json:"last_login"` Location string `json:"location"` Login string `json:"login"` LoginName string `json:"login_name"` ProhibitLogin bool `json:"prohibit_login"` Restricted bool `json:"restricted"` SourceId int `json:"source_id"` StarredReposCount int `json:"starred_repos_count"` Username string `json:"username"` Visibility string `json:"visibility"` Website string `json:"website"` } `json:"owner"` Parent interface{} `json:"parent"` Permissions struct { Admin bool `json:"admin"` Pull bool `json:"pull"` Push bool `json:"push"` } `json:"permissions"` Private bool `json:"private"` ProjectsMode string `json:"projects_mode"` ReleaseCounter int `json:"release_counter"` RepoTransfer interface{} `json:"repo_transfer"` Size int `json:"size"` SshUrl string `json:"ssh_url"` StarsCount int `json:"stars_count"` Template bool `json:"template"` UpdatedAt time.Time `json:"updated_at"` Url string `json:"url"` WatchersCount int `json:"watchers_count"` Website string `json:"website"` } `json:"repository"` Sender struct { Active bool `json:"active"` AvatarUrl string `json:"avatar_url"` Created time.Time `json:"created"` Description string `json:"description"` Email string `json:"email"` FollowersCount int `json:"followers_count"` FollowingCount int `json:"following_count"` FullName string `json:"full_name"` HtmlUrl string `json:"html_url"` Id int `json:"id"` IsAdmin bool `json:"is_admin"` Language string `json:"language"` LastLogin time.Time `json:"last_login"` Location string `json:"location"` Login string `json:"login"` LoginName string `json:"login_name"` ProhibitLogin bool `json:"prohibit_login"` Restricted bool `json:"restricted"` SourceId int `json:"source_id"` StarredReposCount int `json:"starred_repos_count"` Username string `json:"username"` Visibility string `json:"visibility"` Website string `json:"website"` } `json:"sender"` TotalCommits int `json:"total_commits"` }