diff --git a/modules/structs/package.go b/modules/structs/package.go
index 0059535bea..a9a9429de2 100644
--- a/modules/structs/package.go
+++ b/modules/structs/package.go
@@ -16,6 +16,7 @@ type Package struct {
 	Type       string      `json:"type"`
 	Name       string      `json:"name"`
 	Version    string      `json:"version"`
+	HTMLURL    string      `json:"html_url"`
 	// swagger:strfmt date-time
 	CreatedAt time.Time `json:"created_at"`
 }
diff --git a/services/convert/package.go b/services/convert/package.go
index 276856594b..e90ce8a00f 100644
--- a/services/convert/package.go
+++ b/services/convert/package.go
@@ -35,6 +35,7 @@ func ToPackage(ctx context.Context, pd *packages.PackageDescriptor, doer *user_m
 		Name:       pd.Package.Name,
 		Version:    pd.Version.Version,
 		CreatedAt:  pd.Version.CreatedUnix.AsTime(),
+		HTMLURL:    pd.FullWebLink(),
 	}, nil
 }
 
diff --git a/services/webhook/dingtalk.go b/services/webhook/dingtalk.go
index c6fbe2f076..d615e7254f 100644
--- a/services/webhook/dingtalk.go
+++ b/services/webhook/dingtalk.go
@@ -173,6 +173,12 @@ func (d *DingtalkPayload) Release(p *api.ReleasePayload) (api.Payloader, error)
 	return createDingtalkPayload(text, text, "view release", p.Release.HTMLURL), nil
 }
 
+func (d *DingtalkPayload) Package(p *api.PackagePayload) (api.Payloader, error) {
+	text, _ := getPackagePayloadInfo(p, noneLinkFormatter, true)
+
+	return createDingtalkPayload(text, text, "view package", p.Package.HTMLURL), nil
+}
+
 func createDingtalkPayload(title, text, singleTitle, singleURL string) *DingtalkPayload {
 	return &DingtalkPayload{
 		MsgType: "actionCard",
diff --git a/services/webhook/discord.go b/services/webhook/discord.go
index b22bb4f912..e2ac1410b8 100644
--- a/services/webhook/discord.go
+++ b/services/webhook/discord.go
@@ -256,6 +256,12 @@ func (d *DiscordPayload) Release(p *api.ReleasePayload) (api.Payloader, error) {
 	return d.createPayload(p.Sender, text, p.Release.Note, p.Release.HTMLURL, color), nil
 }
 
+func (d *DiscordPayload) Package(p *api.PackagePayload) (api.Payloader, error) {
+	text, color := getPackagePayloadInfo(p, noneLinkFormatter, false)
+
+	return d.createPayload(p.Sender, text, "", p.Package.HTMLURL, color), nil
+}
+
 // GetDiscordPayload converts a discord webhook into a DiscordPayload
 func GetDiscordPayload(p api.Payloader, event webhook_module.HookEventType, meta string) (api.Payloader, error) {
 	s := new(DiscordPayload)
diff --git a/services/webhook/feishu.go b/services/webhook/feishu.go
index 885cde3bc5..359565ef58 100644
--- a/services/webhook/feishu.go
+++ b/services/webhook/feishu.go
@@ -16,7 +16,7 @@ import (
 type (
 	// FeishuPayload represents
 	FeishuPayload struct {
-		MsgType string `json:"msg_type"` // text / post / image / share_chat / interactive
+		MsgType string `json:"msg_type"` // text / post / image / share_chat / interactive / file /audio / media
 		Content struct {
 			Text string `json:"text"`
 		} `json:"content"`
@@ -158,6 +158,12 @@ func (f *FeishuPayload) Release(p *api.ReleasePayload) (api.Payloader, error) {
 	return newFeishuTextPayload(text), nil
 }
 
+func (f *FeishuPayload) Package(p *api.PackagePayload) (api.Payloader, error) {
+	text, _ := getPackagePayloadInfo(p, noneLinkFormatter, true)
+
+	return newFeishuTextPayload(text), nil
+}
+
 // GetFeishuPayload converts a ding talk webhook into a FeishuPayload
 func GetFeishuPayload(p api.Payloader, event webhook_module.HookEventType, _ string) (api.Payloader, error) {
 	return convertPayloader(new(FeishuPayload), p, event)
diff --git a/services/webhook/general.go b/services/webhook/general.go
index b9cc3dc845..91593d377b 100644
--- a/services/webhook/general.go
+++ b/services/webhook/general.go
@@ -230,6 +230,24 @@ func getIssueCommentPayloadInfo(p *api.IssueCommentPayload, linkFormatter linkFo
 	return text, issueTitle, color
 }
 
+func getPackagePayloadInfo(p *api.PackagePayload, linkFormatter linkFormatter, withSender bool) (text string, color int) {
+	refLink := linkFormatter(p.Package.HTMLURL, p.Package.Name+":"+p.Package.Version)
+
+	switch p.Action {
+	case api.HookPackageCreated:
+		text = fmt.Sprintf("Package created: %s", refLink)
+		color = greenColor
+	case api.HookPackageDeleted:
+		text = fmt.Sprintf("Package deleted: %s", refLink)
+		color = redColor
+	}
+	if withSender {
+		text += fmt.Sprintf(" by %s", linkFormatter(setting.AppURL+url.PathEscape(p.Sender.UserName), p.Sender.UserName))
+	}
+
+	return text, color
+}
+
 // ToHook convert models.Webhook to api.Hook
 // This function is not part of the convert package to prevent an import cycle
 func ToHook(repoLink string, w *webhook_model.Webhook) (*api.Hook, error) {
diff --git a/services/webhook/matrix.go b/services/webhook/matrix.go
index a7f57f97b6..ab7e6b72c2 100644
--- a/services/webhook/matrix.go
+++ b/services/webhook/matrix.go
@@ -210,6 +210,21 @@ func (m *MatrixPayload) Repository(p *api.RepositoryPayload) (api.Payloader, err
 	return getMatrixPayload(text, nil, m.MsgType), nil
 }
 
+func (m *MatrixPayload) Package(p *api.PackagePayload) (api.Payloader, error) {
+	senderLink := MatrixLinkFormatter(setting.AppURL+p.Sender.UserName, p.Sender.UserName)
+	repoLink := MatrixLinkFormatter(p.Repository.HTMLURL, p.Repository.FullName)
+	var text string
+
+	switch p.Action {
+	case api.HookPackageCreated:
+		text = fmt.Sprintf("[%s] Package published by %s", repoLink, senderLink)
+	case api.HookPackageDeleted:
+		text = fmt.Sprintf("[%s] Package deleted by %s", repoLink, senderLink)
+	}
+
+	return getMatrixPayload(text, nil, m.MsgType), nil
+}
+
 // GetMatrixPayload converts a Matrix webhook into a MatrixPayload
 func GetMatrixPayload(p api.Payloader, event webhook_module.HookEventType, meta string) (api.Payloader, error) {
 	s := new(MatrixPayload)
diff --git a/services/webhook/msteams.go b/services/webhook/msteams.go
index dfc1c68289..f58da3fe1c 100644
--- a/services/webhook/msteams.go
+++ b/services/webhook/msteams.go
@@ -296,6 +296,20 @@ func (m *MSTeamsPayload) Release(p *api.ReleasePayload) (api.Payloader, error) {
 	), nil
 }
 
+func (m *MSTeamsPayload) Package(p *api.PackagePayload) (api.Payloader, error) {
+	title, color := getPackagePayloadInfo(p, noneLinkFormatter, false)
+
+	return createMSTeamsPayload(
+		p.Repository,
+		p.Sender,
+		title,
+		"",
+		p.Package.HTMLURL,
+		color,
+		&MSTeamsFact{"Package:", p.Package.Name},
+	), nil
+}
+
 // GetMSTeamsPayload converts a MSTeams webhook into a MSTeamsPayload
 func GetMSTeamsPayload(p api.Payloader, event webhook_module.HookEventType, _ string) (api.Payloader, error) {
 	return convertPayloader(new(MSTeamsPayload), p, event)
diff --git a/services/webhook/packagist.go b/services/webhook/packagist.go
index e47e7d3285..714a4c076e 100644
--- a/services/webhook/packagist.go
+++ b/services/webhook/packagist.go
@@ -104,6 +104,10 @@ func (f *PackagistPayload) Release(_ *api.ReleasePayload) (api.Payloader, error)
 	return nil, nil
 }
 
+func (f *PackagistPayload) Package(_ *api.PackagePayload) (api.Payloader, error) {
+	return nil, nil
+}
+
 // GetPackagistPayload converts a packagist webhook into a PackagistPayload
 func GetPackagistPayload(p api.Payloader, event webhook_module.HookEventType, meta string) (api.Payloader, error) {
 	s := new(PackagistPayload)
diff --git a/services/webhook/payloader.go b/services/webhook/payloader.go
index d53e65fa5e..bd482c04ea 100644
--- a/services/webhook/payloader.go
+++ b/services/webhook/payloader.go
@@ -22,6 +22,7 @@ type PayloadConvertor interface {
 	Repository(*api.RepositoryPayload) (api.Payloader, error)
 	Release(*api.ReleasePayload) (api.Payloader, error)
 	Wiki(*api.WikiPayload) (api.Payloader, error)
+	Package(*api.PackagePayload) (api.Payloader, error)
 }
 
 func convertPayloader(s PayloadConvertor, p api.Payloader, event webhook_module.HookEventType) (api.Payloader, error) {
@@ -53,6 +54,8 @@ func convertPayloader(s PayloadConvertor, p api.Payloader, event webhook_module.
 		return s.Release(p.(*api.ReleasePayload))
 	case webhook_module.HookEventWiki:
 		return s.Wiki(p.(*api.WikiPayload))
+	case webhook_module.HookEventPackage:
+		return s.Package(p.(*api.PackagePayload))
 	}
 	return s, nil
 }
diff --git a/services/webhook/slack.go b/services/webhook/slack.go
index 75079d0fdf..ac27b5bc71 100644
--- a/services/webhook/slack.go
+++ b/services/webhook/slack.go
@@ -171,6 +171,12 @@ func (s *SlackPayload) Release(p *api.ReleasePayload) (api.Payloader, error) {
 	return s.createPayload(text, nil), nil
 }
 
+func (s *SlackPayload) Package(p *api.PackagePayload) (api.Payloader, error) {
+	text, _ := getPackagePayloadInfo(p, SlackLinkFormatter, true)
+
+	return s.createPayload(text, nil), nil
+}
+
 // Push implements PayloadConvertor Push method
 func (s *SlackPayload) Push(p *api.PushPayload) (api.Payloader, error) {
 	// n new commits
diff --git a/services/webhook/telegram.go b/services/webhook/telegram.go
index ea7e8185de..1bdc74e183 100644
--- a/services/webhook/telegram.go
+++ b/services/webhook/telegram.go
@@ -186,6 +186,12 @@ func (t *TelegramPayload) Release(p *api.ReleasePayload) (api.Payloader, error)
 	return createTelegramPayload(text), nil
 }
 
+func (t *TelegramPayload) Package(p *api.PackagePayload) (api.Payloader, error) {
+	text, _ := getPackagePayloadInfo(p, htmlLinkFormatter, true)
+
+	return createTelegramPayload(text), nil
+}
+
 // GetTelegramPayload converts a telegram webhook into a TelegramPayload
 func GetTelegramPayload(p api.Payloader, event webhook_module.HookEventType, _ string) (api.Payloader, error) {
 	return convertPayloader(new(TelegramPayload), p, event)
diff --git a/services/webhook/wechatwork.go b/services/webhook/wechatwork.go
index a7680f1c67..80245c7e77 100644
--- a/services/webhook/wechatwork.go
+++ b/services/webhook/wechatwork.go
@@ -179,6 +179,12 @@ func (f *WechatworkPayload) Release(p *api.ReleasePayload) (api.Payloader, error
 	return newWechatworkMarkdownPayload(text), nil
 }
 
+func (f *WechatworkPayload) Package(p *api.PackagePayload) (api.Payloader, error) {
+	text, _ := getPackagePayloadInfo(p, noneLinkFormatter, true)
+
+	return newWechatworkMarkdownPayload(text), nil
+}
+
 // GetWechatworkPayload GetWechatworkPayload converts a ding talk webhook into a WechatworkPayload
 func GetWechatworkPayload(p api.Payloader, event webhook_module.HookEventType, _ string) (api.Payloader, error) {
 	return convertPayloader(new(WechatworkPayload), p, event)
diff --git a/templates/swagger/v1_json.tmpl b/templates/swagger/v1_json.tmpl
index 370bed2b24..93ac282597 100644
--- a/templates/swagger/v1_json.tmpl
+++ b/templates/swagger/v1_json.tmpl
@@ -20029,6 +20029,10 @@
         "creator": {
           "$ref": "#/definitions/User"
         },
+        "html_url": {
+          "type": "string",
+          "x-go-name": "HTMLURL"
+        },
         "id": {
           "type": "integer",
           "format": "int64",