binwiederhier hace 7 meses
padre
commit
4603802f62

La diferencia del archivo ha sido suprimido porque es demasiado grande
+ 384 - 204
docs/template-functions.md


+ 1 - 30
server/server.go

@@ -1183,7 +1183,7 @@ func (s *Server) replaceTemplate(tpl string, source string) (string, error) {
 	if err := json.Unmarshal([]byte(source), &data); err != nil {
 		return "", errHTTPBadRequestTemplateMessageNotJSON
 	}
-	t, err := template.New("").Funcs(sprig.FuncMap()).Parse(tpl)
+	t, err := template.New("").Funcs(sprig.TxtFuncMap()).Parse(tpl)
 	if err != nil {
 		return "", errHTTPBadRequestTemplateInvalid.Wrap("%s", err.Error())
 	}
@@ -2111,32 +2111,3 @@ func (s *Server) updateAndWriteStats(messagesCount int64) {
 		}
 	}()
 }
-
-func loadTemplatesFromDir(dir string) (map[string]*template.Template, error) {
-	templates := make(map[string]*template.Template)
-	entries, err := os.ReadDir(dir)
-	if err != nil {
-		return nil, err
-	}
-	for _, entry := range entries {
-		if entry.IsDir() {
-			continue
-		}
-		name := entry.Name()
-		if !strings.HasSuffix(name, ".tmpl") {
-			continue
-		}
-		path := filepath.Join(dir, name)
-		content, err := os.ReadFile(path)
-		if err != nil {
-			return nil, fmt.Errorf("failed to read template %s: %w", name, err)
-		}
-		tmpl, err := template.New(name).Funcs(sprig.FuncMap()).Parse(string(content))
-		if err != nil {
-			return nil, fmt.Errorf("failed to parse template %s: %w", name, err)
-		}
-		base := strings.TrimSuffix(name, ".tmpl")
-		templates[base] = tmpl
-	}
-	return templates, nil
-}

+ 47 - 22
server/templates/github.yml

@@ -1,31 +1,56 @@
 title: |
-  {{- if .pull_request }}
-  Pull request {{ .action }}: #{{ .pull_request.number }} {{ .pull_request.title }}
-  {{- else if and .starred_at (eq .action "created")}}
-  ⭐ {{ .sender.login }} starred {{ .repository.full_name }}
+  {{- if and .starred_at (eq .action "created")}}
+  ⭐ {{ .sender.login }} starred {{ .repository.name }}
+  
+  {{- else if and .repository (eq .action "started")}}
+  👀 {{ .sender.login }} started watching {{ .repository.name }}
+  
   {{- else if and .comment (eq .action "created") }}
-  💬 New comment on issue #{{ .issue.number }} — {{ .issue.title }}
+  💬 New comment on #{{ .issue.number }}: {{ .issue.title }}
+  
+  {{- else if .pull_request }}
+  🔀 Pull request {{ .action }}: #{{ .pull_request.number }} {{ .pull_request.title }}
+  
+  {{- else if .issue }}
+  🐛 Issue {{ .action }}: #{{ .issue.number }} {{ .issue.title }}
+  
   {{- else }}
-  Unsupported GitHub event type or action.
+  {{ fail "Unsupported GitHub event type or action." }}
   {{- end }}
 message: |
-  {{- if .pull_request }}
-  Repository: {{ .repository.full_name }}, branch {{ .pull_request.head.ref }} → {{ .pull_request.base.ref }}
-  Created by: {{ .pull_request.user.login }}
-  Link: {{ .pull_request.html_url }}
-  {{ if .pull_request.body }}Description:
-  {{ .pull_request.body }}{{ end }}
-  {{- else if and .starred_at (eq .action "created")}}
-  ⭐ {{ .sender.login }} starred {{ .repository.full_name }}
-  📦 {{ .repository.description | default "(no description)" }}
-  🔗 {{ .repository.html_url }}
-  📅 {{ .starred_at }}
+  {{ if and .starred_at (eq .action "created")}}
+  Stargazer: {{ .sender.html_url }}
+  Repository: {{ .repository.html_url }}
+  
+  {{- else if and .repository (eq .action "started")}}
+  Watcher: {{ .sender.html_url }}
+  Repository: {{ .repository.html_url }}
+  
   {{- else if and .comment (eq .action "created") }}
-  💬 New comment on issue #{{ .issue.number }} — {{ .issue.title }}
-  📦 {{ .repository.full_name }}
-  👤 {{ .comment.user.login }}
-  🔗 {{ .comment.html_url }}
-  📝 {{ .comment.body | default "(no comment body)" }}
+  Commenter: {{ .comment.user.html_url }}
+  Repository: {{ .repository.html_url }}
+  Comment link: {{ .comment.html_url }}
+  {{ if .comment.body }}
+  Comment:
+  {{ .comment.body | trunc 2000 }}{{ end }}
+  
+  {{- else if .pull_request }}
+  Branch: {{ .pull_request.head.ref }} → {{ .pull_request.base.ref }}
+  {{ .action | title }} by: {{ .pull_request.user.html_url }}
+  Repository: {{ .repository.html_url }}
+  Pull request: {{ .pull_request.html_url }}
+  {{ if .pull_request.body }}
+  Description:
+  {{ .pull_request.body | trunc 2000 }}{{ end }}
+  
+  {{- else if .issue }}
+  {{ .action | title }} by: {{ .issue.user.html_url }}
+  Repository: {{ .repository.html_url }}
+  Issue link: {{ .issue.html_url }}
+  {{ if .issue.body }}
+  Description:
+  {{ .issue.body | trunc 2000 }}{{ end }}
+  
   {{- else }}
   {{ fail "Unsupported GitHub event type or action." }}
   {{- end }}

+ 216 - 0
server/testdata/webhook_github_issue_opened.json

@@ -0,0 +1,216 @@
+{
+  "action": "opened",
+  "issue": {
+    "url": "https://api.github.com/repos/binwiederhier/ntfy/issues/1391",
+    "repository_url": "https://api.github.com/repos/binwiederhier/ntfy",
+    "labels_url": "https://api.github.com/repos/binwiederhier/ntfy/issues/1391/labels{/name}",
+    "comments_url": "https://api.github.com/repos/binwiederhier/ntfy/issues/1391/comments",
+    "events_url": "https://api.github.com/repos/binwiederhier/ntfy/issues/1391/events",
+    "html_url": "https://github.com/binwiederhier/ntfy/issues/1391",
+    "id": 3236389051,
+    "node_id": "I_kwDOGRBhi87A52C7",
+    "number": 1391,
+    "title": "http 500 error (ntfy error 50001)",
+    "user": {
+      "login": "TheUser-dev",
+      "id": 213207407,
+      "node_id": "U_kgDODLVJbw",
+      "avatar_url": "https://avatars.githubusercontent.com/u/213207407?v=4",
+      "gravatar_id": "",
+      "url": "https://api.github.com/users/TheUser-dev",
+      "html_url": "https://github.com/TheUser-dev",
+      "followers_url": "https://api.github.com/users/TheUser-dev/followers",
+      "following_url": "https://api.github.com/users/TheUser-dev/following{/other_user}",
+      "gists_url": "https://api.github.com/users/TheUser-dev/gists{/gist_id}",
+      "starred_url": "https://api.github.com/users/TheUser-dev/starred{/owner}{/repo}",
+      "subscriptions_url": "https://api.github.com/users/TheUser-dev/subscriptions",
+      "organizations_url": "https://api.github.com/users/TheUser-dev/orgs",
+      "repos_url": "https://api.github.com/users/TheUser-dev/repos",
+      "events_url": "https://api.github.com/users/TheUser-dev/events{/privacy}",
+      "received_events_url": "https://api.github.com/users/TheUser-dev/received_events",
+      "type": "User",
+      "user_view_type": "public",
+      "site_admin": false
+    },
+    "labels": [
+      {
+        "id": 3480884102,
+        "node_id": "LA_kwDOGRBhi87PehOG",
+        "url": "https://api.github.com/repos/binwiederhier/ntfy/labels/%F0%9F%AA%B2%20bug",
+        "name": "🪲 bug",
+        "color": "d73a4a",
+        "default": false,
+        "description": "Something isn't working"
+      }
+    ],
+    "state": "open",
+    "locked": false,
+    "assignee": null,
+    "assignees": [
+    ],
+    "milestone": null,
+    "comments": 0,
+    "created_at": "2025-07-16T15:20:56Z",
+    "updated_at": "2025-07-16T15:20:56Z",
+    "closed_at": null,
+    "author_association": "NONE",
+    "active_lock_reason": null,
+    "sub_issues_summary": {
+      "total": 0,
+      "completed": 0,
+      "percent_completed": 0
+    },
+    "body": ":lady_beetle: **Describe the bug**\nWhen sending a notification (especially when it happens with multiple requests) this error occurs\n\n:computer: **Components impacted**\nntfy server 2.13.0 in docker, debian 12 arm64\n\n:bulb: **Screenshots and/or logs**\n```\nclosed with HTTP 500 (ntfy error 50001) (error=database table is locked, http_method=POST, http_path=/_matrix/push/v1/notify, tag=http, visitor_auth_limiter_limit=0.016666666666666666, visitor_auth_limiter_tokens=30, visitor_id=ip:<edited>, visitor_ip=<edited>, visitor_messages=448, visitor_messages_limit=17280, visitor_messages_remaining=16832, visitor_request_limiter_limit=0.2, visitor_request_limiter_tokens=57.049697891799994, visitor_seen=2025-07-16T15:06:35.429Z)\n```\n\n:crystal_ball: **Additional context**\nLooks like this has already been fixed by #498, regression?\n",
+    "reactions": {
+      "url": "https://api.github.com/repos/binwiederhier/ntfy/issues/1391/reactions",
+      "total_count": 0,
+      "+1": 0,
+      "-1": 0,
+      "laugh": 0,
+      "hooray": 0,
+      "confused": 0,
+      "heart": 0,
+      "rocket": 0,
+      "eyes": 0
+    },
+    "timeline_url": "https://api.github.com/repos/binwiederhier/ntfy/issues/1391/timeline",
+    "performed_via_github_app": null,
+    "state_reason": null
+  },
+  "repository": {
+    "id": 420503947,
+    "node_id": "R_kgDOGRBhiw",
+    "name": "ntfy",
+    "full_name": "binwiederhier/ntfy",
+    "private": false,
+    "owner": {
+      "login": "binwiederhier",
+      "id": 664597,
+      "node_id": "MDQ6VXNlcjY2NDU5Nw==",
+      "avatar_url": "https://avatars.githubusercontent.com/u/664597?v=4",
+      "gravatar_id": "",
+      "url": "https://api.github.com/users/binwiederhier",
+      "html_url": "https://github.com/binwiederhier",
+      "followers_url": "https://api.github.com/users/binwiederhier/followers",
+      "following_url": "https://api.github.com/users/binwiederhier/following{/other_user}",
+      "gists_url": "https://api.github.com/users/binwiederhier/gists{/gist_id}",
+      "starred_url": "https://api.github.com/users/binwiederhier/starred{/owner}{/repo}",
+      "subscriptions_url": "https://api.github.com/users/binwiederhier/subscriptions",
+      "organizations_url": "https://api.github.com/users/binwiederhier/orgs",
+      "repos_url": "https://api.github.com/users/binwiederhier/repos",
+      "events_url": "https://api.github.com/users/binwiederhier/events{/privacy}",
+      "received_events_url": "https://api.github.com/users/binwiederhier/received_events",
+      "type": "User",
+      "user_view_type": "public",
+      "site_admin": false
+    },
+    "html_url": "https://github.com/binwiederhier/ntfy",
+    "description": "Send push notifications to your phone or desktop using PUT/POST",
+    "fork": false,
+    "url": "https://api.github.com/repos/binwiederhier/ntfy",
+    "forks_url": "https://api.github.com/repos/binwiederhier/ntfy/forks",
+    "keys_url": "https://api.github.com/repos/binwiederhier/ntfy/keys{/key_id}",
+    "collaborators_url": "https://api.github.com/repos/binwiederhier/ntfy/collaborators{/collaborator}",
+    "teams_url": "https://api.github.com/repos/binwiederhier/ntfy/teams",
+    "hooks_url": "https://api.github.com/repos/binwiederhier/ntfy/hooks",
+    "issue_events_url": "https://api.github.com/repos/binwiederhier/ntfy/issues/events{/number}",
+    "events_url": "https://api.github.com/repos/binwiederhier/ntfy/events",
+    "assignees_url": "https://api.github.com/repos/binwiederhier/ntfy/assignees{/user}",
+    "branches_url": "https://api.github.com/repos/binwiederhier/ntfy/branches{/branch}",
+    "tags_url": "https://api.github.com/repos/binwiederhier/ntfy/tags",
+    "blobs_url": "https://api.github.com/repos/binwiederhier/ntfy/git/blobs{/sha}",
+    "git_tags_url": "https://api.github.com/repos/binwiederhier/ntfy/git/tags{/sha}",
+    "git_refs_url": "https://api.github.com/repos/binwiederhier/ntfy/git/refs{/sha}",
+    "trees_url": "https://api.github.com/repos/binwiederhier/ntfy/git/trees{/sha}",
+    "statuses_url": "https://api.github.com/repos/binwiederhier/ntfy/statuses/{sha}",
+    "languages_url": "https://api.github.com/repos/binwiederhier/ntfy/languages",
+    "stargazers_url": "https://api.github.com/repos/binwiederhier/ntfy/stargazers",
+    "contributors_url": "https://api.github.com/repos/binwiederhier/ntfy/contributors",
+    "subscribers_url": "https://api.github.com/repos/binwiederhier/ntfy/subscribers",
+    "subscription_url": "https://api.github.com/repos/binwiederhier/ntfy/subscription",
+    "commits_url": "https://api.github.com/repos/binwiederhier/ntfy/commits{/sha}",
+    "git_commits_url": "https://api.github.com/repos/binwiederhier/ntfy/git/commits{/sha}",
+    "comments_url": "https://api.github.com/repos/binwiederhier/ntfy/comments{/number}",
+    "issue_comment_url": "https://api.github.com/repos/binwiederhier/ntfy/issues/comments{/number}",
+    "contents_url": "https://api.github.com/repos/binwiederhier/ntfy/contents/{+path}",
+    "compare_url": "https://api.github.com/repos/binwiederhier/ntfy/compare/{base}...{head}",
+    "merges_url": "https://api.github.com/repos/binwiederhier/ntfy/merges",
+    "archive_url": "https://api.github.com/repos/binwiederhier/ntfy/{archive_format}{/ref}",
+    "downloads_url": "https://api.github.com/repos/binwiederhier/ntfy/downloads",
+    "issues_url": "https://api.github.com/repos/binwiederhier/ntfy/issues{/number}",
+    "pulls_url": "https://api.github.com/repos/binwiederhier/ntfy/pulls{/number}",
+    "milestones_url": "https://api.github.com/repos/binwiederhier/ntfy/milestones{/number}",
+    "notifications_url": "https://api.github.com/repos/binwiederhier/ntfy/notifications{?since,all,participating}",
+    "labels_url": "https://api.github.com/repos/binwiederhier/ntfy/labels{/name}",
+    "releases_url": "https://api.github.com/repos/binwiederhier/ntfy/releases{/id}",
+    "deployments_url": "https://api.github.com/repos/binwiederhier/ntfy/deployments",
+    "created_at": "2021-10-23T19:25:32Z",
+    "updated_at": "2025-07-16T14:54:16Z",
+    "pushed_at": "2025-07-16T11:49:26Z",
+    "git_url": "git://github.com/binwiederhier/ntfy.git",
+    "ssh_url": "git@github.com:binwiederhier/ntfy.git",
+    "clone_url": "https://github.com/binwiederhier/ntfy.git",
+    "svn_url": "https://github.com/binwiederhier/ntfy",
+    "homepage": "https://ntfy.sh",
+    "size": 36831,
+    "stargazers_count": 25112,
+    "watchers_count": 25112,
+    "language": "Go",
+    "has_issues": true,
+    "has_projects": true,
+    "has_downloads": true,
+    "has_wiki": true,
+    "has_pages": false,
+    "has_discussions": false,
+    "forks_count": 984,
+    "mirror_url": null,
+    "archived": false,
+    "disabled": false,
+    "open_issues_count": 369,
+    "license": {
+      "key": "apache-2.0",
+      "name": "Apache License 2.0",
+      "spdx_id": "Apache-2.0",
+      "url": "https://api.github.com/licenses/apache-2.0",
+      "node_id": "MDc6TGljZW5zZTI="
+    },
+    "allow_forking": true,
+    "is_template": false,
+    "web_commit_signoff_required": false,
+    "topics": [
+      "curl",
+      "notifications",
+      "ntfy",
+      "ntfysh",
+      "pubsub",
+      "push-notifications",
+      "rest-api"
+    ],
+    "visibility": "public",
+    "forks": 984,
+    "open_issues": 369,
+    "watchers": 25112,
+    "default_branch": "main"
+  },
+  "sender": {
+    "login": "TheUser-dev",
+    "id": 213207407,
+    "node_id": "U_kgDODLVJbw",
+    "avatar_url": "https://avatars.githubusercontent.com/u/213207407?v=4",
+    "gravatar_id": "",
+    "url": "https://api.github.com/users/TheUser-dev",
+    "html_url": "https://github.com/TheUser-dev",
+    "followers_url": "https://api.github.com/users/TheUser-dev/followers",
+    "following_url": "https://api.github.com/users/TheUser-dev/following{/other_user}",
+    "gists_url": "https://api.github.com/users/TheUser-dev/gists{/gist_id}",
+    "starred_url": "https://api.github.com/users/TheUser-dev/starred{/owner}{/repo}",
+    "subscriptions_url": "https://api.github.com/users/TheUser-dev/subscriptions",
+    "organizations_url": "https://api.github.com/users/TheUser-dev/orgs",
+    "repos_url": "https://api.github.com/users/TheUser-dev/repos",
+    "events_url": "https://api.github.com/users/TheUser-dev/events{/privacy}",
+    "received_events_url": "https://api.github.com/users/TheUser-dev/received_events",
+    "type": "User",
+    "user_view_type": "public",
+    "site_admin": false
+  }
+}

+ 13 - 30
util/sprig/functions.go

@@ -2,40 +2,26 @@ package sprig
 
 import (
 	"errors"
-	"html/template"
+	"golang.org/x/text/cases"
+	"golang.org/x/text/language"
 	"math/rand"
 	"path"
 	"path/filepath"
 	"reflect"
 	"strconv"
 	"strings"
-	ttemplate "text/template"
+	"text/template"
 	"time"
-
-	"golang.org/x/text/cases"
 )
 
-// FuncMap produces the function map.
+// TxtFuncMap produces the function map.
 //
 // Use this to pass the functions into the template engine:
 //
 //	tpl := template.New("foo").Funcs(sprig.FuncMap()))
-func FuncMap() template.FuncMap {
-	return HTMLFuncMap()
-}
-
+//
 // TxtFuncMap returns a 'text/template'.FuncMap
-func TxtFuncMap() ttemplate.FuncMap {
-	return GenericFuncMap()
-}
-
-// HTMLFuncMap returns an 'html/template'.Funcmap
-func HTMLFuncMap() template.FuncMap {
-	return GenericFuncMap()
-}
-
-// GenericFuncMap returns a copy of the basic function map as a map[string]any.
-func GenericFuncMap() map[string]any {
+func TxtFuncMap() template.FuncMap {
 	gfm := make(map[string]any, len(genericMap))
 	for k, v := range genericMap {
 		gfm[k] = v
@@ -63,11 +49,13 @@ var genericMap = map[string]any{
 	"unixEpoch":        unixEpoch,
 
 	// Strings
-	"trunc":  trunc,
-	"trim":   strings.TrimSpace,
-	"upper":  strings.ToUpper,
-	"lower":  strings.ToLower,
-	"title":  cases.Title,
+	"trunc": trunc,
+	"trim":  strings.TrimSpace,
+	"upper": strings.ToUpper,
+	"lower": strings.ToLower,
+	"title": func(s string) string {
+		return cases.Title(language.English).String(s)
+	},
 	"substr": substring,
 	// Switch order so that "foo" | repeat 5
 	"repeat": func(count int, str string) string { return strings.Repeat(str, count) },
@@ -99,11 +87,6 @@ var genericMap = map[string]any{
 	"seq":       seq,
 	"toDecimal": toDecimal,
 
-	//"gt": func(a, b int) bool {return a > b},
-	//"gte": func(a, b int) bool {return a >= b},
-	//"lt": func(a, b int) bool {return a < b},
-	//"lte": func(a, b int) bool {return a <= b},
-
 	// split "/" foo/bar returns map[int]string{0: foo, 1: bar}
 	"split":     split,
 	"splitList": func(sep, orig string) []string { return strings.Split(orig, sep) },

Algunos archivos no se mostraron porque demasiados archivos cambiaron en este cambio