├── .github
├── CODEOWNERS
├── tcdiscordwebhooks-logo-1024.png
├── PULL_REQUEST_TEMPLATE.md
├── ISSUE_TEMPLATE
│ ├── feature_request.md
│ └── bug_report.md
└── workflows
│ ├── git.yml
│ ├── ci.yml
│ └── stale.yml
├── .editorconfig
├── docker-compose.yml
├── tc-discord-webhooks-server
├── src
│ ├── main
│ │ ├── java
│ │ │ └── com
│ │ │ │ └── github
│ │ │ │ └── playerforcehd
│ │ │ │ └── tcdiscordwebhooks
│ │ │ │ ├── AppServer.java
│ │ │ │ ├── discord
│ │ │ │ ├── embeds
│ │ │ │ │ ├── DiscordEmbedImage.java
│ │ │ │ │ ├── DiscordEmbedFooter.java
│ │ │ │ │ ├── DiscordEmbedField.java
│ │ │ │ │ ├── DiscordAuthorEmbed.java
│ │ │ │ │ ├── DiscordEmbedColor.java
│ │ │ │ │ └── DiscordEmbed.java
│ │ │ │ ├── DiscordWebHookPayload.java
│ │ │ │ └── DiscordWebHookProcessor.java
│ │ │ │ └── notificator
│ │ │ │ └── DiscordNotificator.java
│ │ └── resources
│ │ │ └── META-INF
│ │ │ └── build-server-plugin-tc-discord-webhooks.xml
│ └── test
│ │ └── java
│ │ └── com
│ │ └── gtihub
│ │ └── playerforcehd
│ │ └── tcdiscordwebhooks
│ │ ├── discord
│ │ └── DiscordEmbedColorTest.java
│ │ └── DiscordWebHookTest.java
└── pom.xml
├── SECURITY.md
├── Makefile
├── .gitignore
├── teamcity-plugin.xml
├── commitlint.config.js
├── CODE_OF_CONDUCT.md
├── README.MD
├── CONTRIBUTING.md
├── pom.xml
└── LICENSE
/.github/CODEOWNERS:
--------------------------------------------------------------------------------
1 | * @pascal-zarrad
2 |
--------------------------------------------------------------------------------
/.github/tcdiscordwebhooks-logo-1024.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/pascal-zarrad/tc-discord-webhooks/HEAD/.github/tcdiscordwebhooks-logo-1024.png
--------------------------------------------------------------------------------
/.editorconfig:
--------------------------------------------------------------------------------
1 | # editorconfig.org
2 | root = true
3 |
4 | [*]
5 | indent_style = space
6 | indent_size = 4
7 |
8 | end_of_line = lf
9 | charset = utf-8
10 | trim_trailing_whitespace = true
11 | insert_final_newline = true
12 |
13 | [*.md]
14 | trim_trailing_whitespace = false
--------------------------------------------------------------------------------
/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: '2'
2 |
3 | services:
4 | teamcity:
5 | image: jetbrains/teamcity-server:latest
6 | volumes:
7 | - ./target/:/data/teamcity_server/datadir/plugins/
8 | ports:
9 | - "8080:8111"
10 |
11 | teamcity_agent:
12 | image: jetbrains/teamcity-agent:latest
13 | depends_on:
14 | - teamcity
15 | links:
16 | - teamcity:teamcity
17 | environment:
18 | - SERVER_URL=teamcity:8111
19 | - AGENT_NAME="tcagent_docker"
20 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 | ### Description
2 | What does your PR solve? If new features where added by your PR, what are the features?
3 |
4 | ### Issue relations
5 | To which issue(s) is your PR related? Use keywords (for example "Closes #1") to link your issues properly.
6 |
7 | ### Test Scenarios
8 | Describe how reviewers can verify that your changes do work from a functional perspective.
9 |
10 | ### Additional information
11 | Add here additional information if applicable or remove this section.
12 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: 'C1 - enhancement'
6 | assignees: pascal-zarrad
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: 'C2 - bug'
6 | assignees: pascal-zarrad
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behavior:
15 | 1. Execute command "..."
16 | 2. Type "..."
17 | 4. See error
18 |
19 | **Expected behavior**
20 | A clear and concise description of what you expected to happen.
21 |
22 | **Screenshots**
23 | If applicable, add screenshots to help explain your problem.
24 |
25 | **Environment:**
26 | - OS: [e.g. Linux]
27 | - Dist: [e.g. Ubuntu]
28 | - Shell: [e.g. Bash]
29 |
30 | **Additional context**
31 | Add any other context about the problem here.
32 |
33 | **Don't forget to add labels!**
34 |
--------------------------------------------------------------------------------
/tc-discord-webhooks-server/src/main/java/com/github/playerforcehd/tcdiscordwebhooks/AppServer.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2021 Pascal Zarrad
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.github.playerforcehd.tcdiscordwebhooks;
18 |
19 | public class AppServer {
20 | }
21 |
--------------------------------------------------------------------------------
/.github/workflows/git.yml:
--------------------------------------------------------------------------------
1 | # Checks for Git branches and commit messages to keep consistency on the repo
2 | name: Git
3 |
4 | on: ['pull_request']
5 |
6 | jobs:
7 | # Verify that the branch name matches the format expected for contributions
8 | verify_branch_name:
9 | runs-on: ubuntu-latest
10 | steps:
11 | - name: Check branch name
12 | uses: deepakputhraya/action-branch-name@master
13 | with:
14 | regex: '([a-z])+\/([a-z])+'
15 | allowed_prefixes: 'feature,bug,release'
16 | ignore: master,develop
17 | verify_commit_messages:
18 | runs-on: ubuntu-18.04
19 | steps:
20 | - uses: actions/checkout@v2
21 | with:
22 | fetch-depth: 0
23 | - uses: wagoid/commitlint-github-action@v2
24 | with:
25 | configFile: commitlint.config.js
26 |
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 | # Security Policy
2 |
3 | ## Supported Versions
4 |
5 | | Version | Supported |
6 | | ------- | ------------------ |
7 | | 1.x.x | :white_check_mark: |
8 |
9 | Legend: :white_check_mark: supported - :ballot_box_with_check: support on critical issues - :x: no longer supported
10 |
11 | ## Reporting a Vulnerability
12 |
13 | If you find a security issue, we advise you to _not_ create an issue on GitHub for it as
14 | it could put all TeamCity installations using TC-Discord-Webhooks on risk.
15 | Please send an e-mail to [this mail][security-mail] to report such issues.
16 |
17 | We take security issues serious and will not create an GitHub issue for any critical security issue
18 | until we have supplied a fix. All communication with the contributor of the issue will be held through e-mail
19 | until we are at a point where we can release information about the issue publicly.
20 |
21 | [security-mail]: mailto:P.Zarrad@outlook.de
22 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on: [push, pull_request]
4 |
5 | jobs:
6 | build:
7 | runs-on: ubuntu-latest
8 | steps:
9 | - uses: actions/checkout@v2
10 | - name: Set up JDK 1.8
11 | uses: actions/setup-java@v1
12 | with:
13 | java-version: 1.8
14 | - name: Build with Maven
15 | run: mvn -B package --file pom.xml
16 | - name: Upload build artifact
17 | uses: actions/upload-artifact@v2
18 | with:
19 | name: tc-discord-webhooks.zip
20 | path: target/tc-discord-webhooks.zip
21 | - name: Upload jacoco coverage report
22 | uses: actions/upload-artifact@v2
23 | with:
24 | name: code-coverage-report
25 | path: tc-discord-webhooks-server/target/site
26 | - name: Upload surefire report
27 | uses: actions/upload-artifact@v2
28 | with:
29 | name: code-coverage-report
30 | path: tc-discord-webhooks-server/target/surefire-reports
31 |
--------------------------------------------------------------------------------
/.github/workflows/stale.yml:
--------------------------------------------------------------------------------
1 |
2 | # Mark and close stale issues to keep issues clean
3 | name: Close stale issues and pull requests
4 |
5 | on:
6 | schedule:
7 | - cron: '0 0 * * *'
8 |
9 | jobs:
10 | stale:
11 | runs-on: ubuntu-latest
12 | steps:
13 | - uses: actions/stale@v3.0.5
14 | with:
15 | repo-token: ${{ secrets.GITHUB_TOKEN }}
16 | stale-issue-message: 'This issue did not have any activity in the last 60 days. It has been marked as stale and will be closed soon.'
17 | stale-pr-message: 'This pull request did not have any activity in the last 60 days. It has been marked as stale and will be closed soon.'
18 | stale-issue-label: 'S2 - stale - no activity'
19 | stale-pr-label: 'S2 - stale - no activity'
20 | exempt-issue-labels: 'S6 - in progress'
21 | exempt-pr-labels: 'S6 - in progress'
22 | days-before-stale: 60
23 | days-before-close: 7
24 |
--------------------------------------------------------------------------------
/tc-discord-webhooks-server/src/main/resources/META-INF/build-server-plugin-tc-discord-webhooks.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | # Makefile to control the local development environment
2 | PWD=$(shell pwd)
3 |
4 | .PHONY: cache_dir
5 | cache_dir:
6 | mkdir -p m2
7 |
8 | # Build the project
9 | .PHONY: build
10 | build: cache_dir
11 | docker run -it --rm -u 1000 -v "$(PWD):/srv/tcdwbuild:delegated" -v "$(PWD)/m2":/var/maven/.m2 -w /srv/tcdwbuild -e MAVEN_CONFIG=/var/maven/.m2 maven:3.6-amazoncorretto-8 mvn clean package
12 |
13 | # Stop the local Docker stack, rebuild the project and start the local Docker stack again
14 | .PHONY: redeploy cache_dir
15 | redeploy:
16 | docker-compose stop
17 | docker run -it --rm -u 1000 -v "$(PWD):/srv/tcdwbuild:delegated" -v "$(PWD)/m2":/var/maven/.m2 -w /srv/tcdwbuild -e MAVEN_CONFIG=/var/maven/.m2 maven:3.6-amazoncorretto-8 mvn clean package
18 | docker-compose up -d
19 |
20 | # Start the local development environment
21 | .PHONY: up
22 | up:
23 | docker-compose up -d
24 |
25 | # Stop the local development environment
26 | .PHONY: stop
27 | stop:
28 | docker-compose stop
29 |
30 | # Clear the local development environment
31 | .PHONY: down
32 | down:
33 | docker-compose down
34 |
35 | # Rebuild the local development environment
36 | .PHONY: rebuild_docker
37 | rebuild_docker:
38 | docker-compose up -d --build --force-recreate
39 |
40 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Created by .ignore support plugin (hsz.mobi)
2 | ### Maven template
3 | target/
4 | pom.xml.tag
5 | pom.xml.releaseBackup
6 | pom.xml.versionsBackup
7 | pom.xml.next
8 | settings.xml
9 | release.properties
10 | dependency-reduced-pom.xml
11 | buildNumber.properties
12 | .mvn/timing.properties
13 | m2/
14 |
15 | # Exclude maven wrapper
16 | !/.mvn/wrapper/maven-wrapper.jar
17 | ### JetBrains template
18 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
19 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
20 |
21 | # Entire .idea folde
22 | .idea
23 |
24 | ## File-based project format:
25 | *.iws
26 | *.iml
27 |
28 | ## Plugin-specific files:
29 |
30 | # IntelliJ
31 | /out/
32 |
33 | gcaptchavalidator.iml
34 |
35 | # mpeltonen/sbt-idea plugin
36 | .idea_modules/
37 |
38 | # JIRA plugin
39 | atlassian-ide-plugin.xml
40 |
41 | # Crashlytics plugin (for Android Studio and IntelliJ)
42 | com_crashlytics_export_strings.xml
43 | crashlytics.properties
44 | crashlytics-build.properties
45 | fabric.properties
46 | ### Java template
47 | *.class
48 |
49 | # BlueJ files
50 | *.ctxt
51 |
52 | # Mobile Tools for Java (J2ME)
53 | .mtj.tmp/
54 |
55 | # Package Files #
56 | *.jar
57 | *.war
58 | *.ear
59 |
60 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
61 | hs_err_pid*
62 |
--------------------------------------------------------------------------------
/tc-discord-webhooks-server/src/main/java/com/github/playerforcehd/tcdiscordwebhooks/discord/embeds/DiscordEmbedImage.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2021 Pascal Zarrad
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.github.playerforcehd.tcdiscordwebhooks.discord.embeds;
18 |
19 | /**
20 | * An image element for a {@link DiscordEmbed}.
21 | * This image element provides only the url attribute, due to
22 | * the fact that Discord's WebHooks do not support any of the other
23 | * attributes.
24 | *
25 | * @author Pascal Zarrad
26 | */
27 | public class DiscordEmbedImage {
28 |
29 | /**
30 | * The url of the image to use
31 | */
32 | private String url;
33 |
34 | public DiscordEmbedImage(String url) {
35 | this.url = url;
36 | }
37 |
38 | public DiscordEmbedImage() {
39 | }
40 |
41 | public String getUrl() {
42 | return url;
43 | }
44 |
45 | public void setUrl(String url) {
46 | this.url = url;
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/teamcity-plugin.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
20 |
21 | tc-discord-webhooks
22 | TC Discord WebHooks
23 | @Version@
24 | A TeamCity plugin that allows the easy creation of Discord WebHooks that are used to notify users
25 | on a Discord server about the current state of builds.
26 |
27 | https://plugins.jetbrains.com/plugin/12608-tc-discord-webhooks
28 | contact@pascal-zarrad.de
29 |
30 | @VendorName@
31 | @VendorURL@
32 | https://raw.githubusercontent.com/pascal-zarrad/tc-discord-webhooks/develop/.github/tcdiscordwebhooks-logo-1024.png
33 |
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/commitlint.config.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2021 Pascal Zarrad
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | // Configuration of commitlint to check commit message guidelines
18 | module.exports = {
19 | parserPreset: 'conventional-changelog-conventionalcommits',
20 | rules: {
21 | 'subject-max-length': [2, 'always', 50],
22 | 'subject-case': [
23 | 2,
24 | 'never',
25 | ['sentence-case', 'start-case'],
26 | ],
27 | 'subject-empty': [2, 'never'],
28 | 'subject-full-stop': [2, 'never', '.'],
29 | 'type-case': [2, 'always', 'lower-case'],
30 | 'type-empty': [2, 'never'],
31 | 'type-enum': [
32 | 2,
33 | 'always',
34 | [
35 | 'feat',
36 | 'fix',
37 | 'perf',
38 | 'refactor',
39 | 'cs',
40 | 'test',
41 | 'build',
42 | 'ci',
43 | 'docs',
44 | 'changelog',
45 | 'bump'
46 | ],
47 | ],
48 | 'scope-empty': [2, 'always'],
49 | 'header-max-length': [2, 'always', 75],
50 | 'body-leading-blank': [1, 'always'],
51 | 'body-max-line-length': [2, 'always', 75],
52 | 'footer-leading-blank': [1, 'always'],
53 | 'footer-max-line-length': [2, 'always', 75]
54 | },
55 | };
56 |
--------------------------------------------------------------------------------
/tc-discord-webhooks-server/src/main/java/com/github/playerforcehd/tcdiscordwebhooks/discord/embeds/DiscordEmbedFooter.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2021 Pascal Zarrad
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.github.playerforcehd.tcdiscordwebhooks.discord.embeds;
18 |
19 | /**
20 | * A footer element for a {@link DiscordEmbed}
21 | *
22 | * @author Pascal Zarrad
23 | */
24 | public class DiscordEmbedFooter {
25 |
26 | /**
27 | * The text that the footer should contain
28 | */
29 | private String text;
30 |
31 | /**
32 | * The url to the icon of the footer
33 | */
34 | private String icon_url;
35 |
36 | public DiscordEmbedFooter(String text, String icon_url) {
37 | this.text = text;
38 | this.icon_url = icon_url;
39 | }
40 |
41 | public DiscordEmbedFooter() {
42 | }
43 |
44 | public DiscordEmbedFooter(String text) {
45 | this.text = text;
46 | }
47 |
48 | public String getText() {
49 | return text;
50 | }
51 |
52 | public void setText(String text) {
53 | this.text = text;
54 | }
55 |
56 | public String getIcon_url() {
57 | return icon_url;
58 | }
59 |
60 | public void setIcon_url(String icon_url) {
61 | this.icon_url = icon_url;
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/tc-discord-webhooks-server/src/main/java/com/github/playerforcehd/tcdiscordwebhooks/discord/embeds/DiscordEmbedField.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2021 Pascal Zarrad
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.github.playerforcehd.tcdiscordwebhooks.discord.embeds;
18 |
19 | /**
20 | * Fields that can be added to an {@link DiscordEmbed}
21 | * to display embedded text
22 | *
23 | * @author Pascal Zarrad
24 | */
25 | public class DiscordEmbedField {
26 |
27 | /**
28 | * The name of the embed field
29 | */
30 | private String name;
31 |
32 | /**
33 | * The value of the embed field
34 | */
35 | private String value;
36 |
37 | /**
38 | * Decides whether the field should be displayed inline or not
39 | */
40 | private boolean inline;
41 |
42 | public DiscordEmbedField(String name, String value) {
43 | this.name = name;
44 | this.value = value;
45 | }
46 |
47 | public DiscordEmbedField(String name, String value, boolean inline) {
48 | this.name = name;
49 | this.value = value;
50 | this.inline = inline;
51 | }
52 |
53 | public DiscordEmbedField() {
54 | }
55 |
56 | public String getName() {
57 | return name;
58 | }
59 |
60 | public void setName(String name) {
61 | this.name = name;
62 | }
63 |
64 | public String getValue() {
65 | return value;
66 | }
67 |
68 | public void setValue(String value) {
69 | this.value = value;
70 | }
71 |
72 | public boolean isInline() {
73 | return inline;
74 | }
75 |
76 | public void setInline(boolean inline) {
77 | this.inline = inline;
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/tc-discord-webhooks-server/src/main/java/com/github/playerforcehd/tcdiscordwebhooks/discord/embeds/DiscordAuthorEmbed.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2021 Pascal Zarrad
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.github.playerforcehd.tcdiscordwebhooks.discord.embeds;
18 |
19 | /**
20 | * An element to set the author of an {@link DiscordEmbed}.
21 | *
22 | * @author Pascal Zarrad
23 | */
24 | public class DiscordAuthorEmbed {
25 |
26 | /**
27 | * The name of the author
28 | */
29 | private String name;
30 |
31 | /**
32 | * The url of the author
33 | */
34 | private String url;
35 |
36 | /**
37 | * The url to the icon of the author
38 | */
39 | private String icon_url;
40 |
41 | public DiscordAuthorEmbed(String name, String url, String icon_url) {
42 | this.name = name;
43 | this.url = url;
44 | this.icon_url = icon_url;
45 | }
46 |
47 | public DiscordAuthorEmbed(String name, String url) {
48 | this.name = name;
49 | this.url = url;
50 | }
51 |
52 | public DiscordAuthorEmbed(String name) {
53 | this.name = name;
54 | }
55 |
56 | public DiscordAuthorEmbed() {
57 | }
58 |
59 | public String getName() {
60 | return name;
61 | }
62 |
63 | public void setName(String name) {
64 | this.name = name;
65 | }
66 |
67 | public String getUrl() {
68 | return url;
69 | }
70 |
71 | public void setUrl(String url) {
72 | this.url = url;
73 | }
74 |
75 | public String getIcon_url() {
76 | return icon_url;
77 | }
78 |
79 | public void setIcon_url(String icon_url) {
80 | this.icon_url = icon_url;
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/tc-discord-webhooks-server/src/main/java/com/github/playerforcehd/tcdiscordwebhooks/discord/embeds/DiscordEmbedColor.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2021 Pascal Zarrad
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.github.playerforcehd.tcdiscordwebhooks.discord.embeds;
18 |
19 | /**
20 | * Provides the basic colors that can be used for embeds.
21 | * The values of the colors are their decimal value.
22 | *
23 | * Also this class provides a method to get the decimal value of a
24 | *
25 | * @author Pascal Zarrad
26 | */
27 | public class DiscordEmbedColor {
28 |
29 | /**
30 | * Red color
31 | */
32 | public static final int RED = 16711680;
33 |
34 | /**
35 | * Blue color
36 | */
37 | public static final int BLUE = 26367;
38 |
39 | /**
40 | * Green color
41 | */
42 | public static final int GREEN = 510208;
43 |
44 | /**
45 | * Yellow color
46 | */
47 | public static final int YELLOW = 15924992;
48 |
49 | /**
50 | * Orange color
51 | */
52 | public static final int ORANGE = 16746496;
53 |
54 | /**
55 | * Converts a hexadecimal color code to a decimal color code.
56 | * This method supports hexadecimal strings as parameter with and without a leading #.
57 | *
58 | * @param hexCode The hexadecimal color code to convert to a decimal value
59 | * @return The decimal value
60 | */
61 | public static int convertHexToDecColor(String hexCode) throws NumberFormatException {
62 | if (hexCode.startsWith("#")) {
63 | String pureHex = hexCode.substring(1);
64 | return Integer.parseInt(pureHex, 16);
65 | } else {
66 | return Integer.parseInt(hexCode, 16);
67 | }
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/tc-discord-webhooks-server/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
20 | 4.0.0
21 |
22 | tc-discord-webhooks
23 | com.github.playerforcehd
24 | 1.1.1
25 |
26 | tc-discord-webhooks-server
27 | jar
28 |
29 |
30 |
31 | org.jetbrains.teamcity
32 | server-api
33 | ${teamcity-version}
34 | provided
35 |
36 |
37 |
38 | org.jetbrains.teamcity
39 | server-web-api
40 | ${teamcity-version}
41 | war
42 | provided
43 |
44 |
45 |
46 | org.jetbrains.teamcity
47 | tests-support
48 | ${teamcity-version}
49 | test
50 |
51 |
52 |
53 |
54 |
55 |
56 | org.apache.maven.plugins
57 | maven-compiler-plugin
58 |
59 | 1.8
60 | 1.8
61 |
62 |
63 |
64 |
65 |
66 |
--------------------------------------------------------------------------------
/tc-discord-webhooks-server/src/test/java/com/gtihub/playerforcehd/tcdiscordwebhooks/discord/DiscordEmbedColorTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2021 Pascal Zarrad
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.gtihub.playerforcehd.tcdiscordwebhooks.discord;
18 |
19 | import com.github.playerforcehd.tcdiscordwebhooks.discord.embeds.DiscordEmbedColor;
20 | import org.testng.annotations.Test;
21 |
22 | import static org.testng.Assert.assertEquals;
23 | import static org.testng.Assert.fail;
24 |
25 | /**
26 | * Tests the {@link DiscordEmbedColor#convertHexToDecColor(String)} method which
27 | * is used to convert hexadecimal color codes into their decimal equivalent.
28 | *
29 | * @author Pascal Zarrad
30 | */
31 | public class DiscordEmbedColorTest {
32 |
33 | /**
34 | * The color code used for the check
35 | */
36 | private final String hexColor = "db2525";
37 |
38 | /**
39 | * Test if the conversion of a hexadecimal number to a decimal number does work properly.
40 | */
41 | @Test
42 | public void testConversionOfHexToDecColor() {
43 | try {
44 | int decimalColorCode = DiscordEmbedColor.convertHexToDecColor(this.hexColor);
45 | assertEquals(decimalColorCode, 14361893, "Expected decimal color code to be 14361893 but got " + decimalColorCode + "!");
46 | } catch (NumberFormatException e) {
47 | fail("Failed to convert hexadecimal to decimal color code.", e);
48 | }
49 | }
50 |
51 | /**
52 | * Test if the conversion of a hexadecimal number with a leading diamond
53 | * to a decimal number does work properly.
54 | */
55 | @Test
56 | public void testConversionOfHexToDecColorWithLeadingDiamond() {
57 | try {
58 | String hexColorWithDiamond = "#" + this.hexColor;
59 | int decimalColorCode = DiscordEmbedColor.convertHexToDecColor(hexColorWithDiamond);
60 | assertEquals(decimalColorCode, 14361893, "Expected decimal color code to be 14361893 but got " + decimalColorCode + "!");
61 | } catch (NumberFormatException e) {
62 | fail("Failed to convert hexadecimal to decimal color code.", e);
63 | }
64 | }
65 |
66 | /**
67 | * Tests if entering an invalid hexadecimal string ends up in a {@link NumberFormatException}
68 | */
69 | @Test
70 | public void testConversionFailsWhenEnteringInvalidHexString() {
71 | String invalidHexadecimalString = "a24fZ"; // Z is not a hexadecimal digit
72 | try {
73 | DiscordEmbedColor.convertHexToDecColor(invalidHexadecimalString);
74 | fail("Entering an invalid hexadecimal string should result in a NumberFormatException!");
75 | } catch (Exception e) {
76 | if (!(e instanceof NumberFormatException)) {
77 | fail("The thrown exception was expected to be a NumberFormatException but got any other exception!", e);
78 | }
79 | }
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | # Contributor Covenant Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as
6 | contributors and maintainers pledge to making participation in our project and
7 | our community a harassment-free experience for everyone, regardless of age, body
8 | size, disability, ethnicity, sex characteristics, gender identity and expression,
9 | level of experience, education, socio-economic status, nationality, personal
10 | appearance, race, religion, or sexual identity and orientation.
11 |
12 | ## Our Standards
13 |
14 | Examples of behavior that contributes to creating a positive environment
15 | include:
16 |
17 | * Using welcoming and inclusive language
18 | * Being respectful of differing viewpoints and experiences
19 | * Gracefully accepting constructive criticism
20 | * Focusing on what is best for the community
21 | * Showing empathy towards other community members
22 |
23 | Examples of unacceptable behavior by participants include:
24 |
25 | * The use of sexualized language or imagery and unwelcome sexual attention or
26 | advances
27 | * Trolling, insulting/derogatory comments, and personal or political attacks
28 | * Public or private harassment
29 | * Publishing others' private information, such as a physical or electronic
30 | address, without explicit permission
31 | * Other conduct which could reasonably be considered inappropriate in a
32 | professional setting
33 |
34 | ## Our Responsibilities
35 |
36 | Project maintainers are responsible for clarifying the standards of acceptable
37 | behavior and are expected to take appropriate and fair corrective action in
38 | response to any instances of unacceptable behavior.
39 |
40 | Project maintainers have the right and responsibility to remove, edit, or
41 | reject comments, commits, code, wiki edits, issues, and other contributions
42 | that are not aligned to this Code of Conduct, or to ban temporarily or
43 | permanently any contributor for other behaviors that they deem inappropriate,
44 | threatening, offensive, or harmful.
45 |
46 | ## Scope
47 |
48 | This Code of Conduct applies both within project spaces and in public spaces
49 | when an individual is representing the project or its community. Examples of
50 | representing a project or community include using an official project e-mail
51 | address, posting via an official social media account, or acting as an appointed
52 | representative at an online or offline event. Representation of a project may be
53 | further defined and clarified by project maintainers.
54 |
55 | ## Enforcement
56 |
57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be
58 | reported by contacting the project team at p.zarrad@outlook.de. All
59 | complaints will be reviewed and investigated and will result in a response that
60 | is deemed necessary and appropriate to the circumstances. The project team is
61 | obligated to maintain confidentiality with regard to the reporter of an incident.
62 | Further details of specific enforcement policies may be posted separately.
63 |
64 | Project maintainers who do not follow or enforce the Code of Conduct in good
65 | faith may face temporary or permanent repercussions as determined by other
66 | members of the project's leadership.
67 |
68 | ## Attribution
69 |
70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
72 |
73 | [homepage]: https://www.contributor-covenant.org
74 |
75 | For answers to common questions about this code of conduct, see
76 | https://www.contributor-covenant.org/faq
77 |
--------------------------------------------------------------------------------
/README.MD:
--------------------------------------------------------------------------------
1 | # TC Discord WebHooks [](https://travis-ci.org/pascal-zarrad/tc-discord-webhooks)
2 |
3 | > **This plugin is in a community maintainance state**
4 | >
5 | > I use GitHub Actions instead of TeamCity right now and don't have time to maintain or update this plugin by myself.
6 | > I decided to discontinue the development of this plugin.
7 | > As of 2021/12/06, this plugin still works and can be installed manually.
8 | >
9 | > I won't provide any maintainance of the plugin by myself. If anyone wants to keep it up to date or contribute code (features, fixes, etc...), I'd be happy about that. Feel always free to do that if you want to.
10 | > I will continue to manage contributions and publish new versions of the plugin.
11 |
12 | ## About
13 | A TeamCity plugin which allows the easy creation of Discord WebHooks to notify users on a Discord server about the current build status of projects.
14 |
15 | **Features:**
16 | - Discord Integration to get notified on a Discord server
17 | - Simple setup
18 | - Beautiful messages out of the box
19 |
20 | ## System Requirements
21 | To use this plugin you need at least TeamCity 2018.2 and Java 8.
22 | In addition, a Discord server is necessary as you can't have webhooks without a server.
23 |
24 | ## Installation
25 | Simply put the plugins ZIP file into the plugin's directory of your TeamCity server and restart it.
26 | You should also be able to upload the plugin using the settings page of your TeamCity installation.
27 | For further information refer to the official JetBrains documentation: [Installing additional plugins](https://www.jetbrains.com/help/teamcity/installing-additional-plugins.html)
28 |
29 | ## Usage
30 | The usage of this plugin is simple.
31 | Just create a webhook for one of your Discord channels as shown here: [Discord WebHook HowTo](https://support.discordapp.com/hc/en-us/articles/228383668-Intro-to-Webhooks).
32 |
33 | Then enter the WebHook URL and a Username (recommended to also enter the name!) under "My Settings & Tools" -> "Notification Rules" -> "Discord WebHook".
34 | Configure your rules as normal. You should now receive notifications on Discord!
35 |
36 | If you use a proxy, ensure that `http.proxyHost` and `http.proxyPort` are set properly.
37 |
38 | ## Development on Linux
39 |
40 | ### Prerequisites
41 | To develop and build TC Discord Webhooks on Linux you need the following tools:
42 | - Docker
43 | - docker-compose
44 | - make
45 |
46 | Note that you might also just use the `mvn` command of your local Maven 2 installation to build the project.
47 | But using the Makefile ensures that you have your environment configured properly.
48 |
49 | Also ensure your local system is configured properly.
50 | If you encounter issues while downloading Maven dependencies, you might check [this](https://stackoverflow.com/a/45644890)
51 | StackOverflow comment that might help you to fix the issue.
52 |
53 | **Use the following setup ONLY for development purposes! It is not production ready!**
54 |
55 | ### Building
56 |
57 | Use the build target of the Makefile to build the project:
58 | ```
59 | make build
60 | ```
61 | To build the project without `make`, simply run the `package` goal of Maven.
62 |
63 | ### Running the TeamCity development server
64 |
65 | Start the local TeamCity server:
66 | ```
67 | make up
68 | ```
69 |
70 | Stop the local TeamCity server:
71 | ```
72 | make stop
73 | ```
74 |
75 | Delete the containers of the local TeamCity server:
76 | ```
77 | make down
78 | ```
79 |
80 | Rebuild the plugin and restart the TeamCity server:
81 | ```
82 | make redeploy
83 | ```
84 |
85 | Rebuild the local Docker stack:
86 | ```
87 | make rebuild_docker
88 | ```
89 |
90 | ## Development on Windows
91 |
92 | ### Prerequisites
93 | To develop and build TC Discord Webhooks on Linux you need the following tools:
94 | - Docker Desktop
95 | - Maven 2
96 |
97 | **Use the following setup ONLY for development purposes! It is not production ready!**
98 |
99 | ### Building
100 |
101 | To build the project without Docker, simply run the `package` goal of Maven:
102 | ```
103 | mvn clean package
104 | ```
105 |
106 | ### Running the TeamCity development server
107 |
108 | To set up the containers and start the Docker stack, take a look at the commands below.
109 | After the containers have been started you can open TeamCity in your browser.
110 | TeamCity should be reachable at `http://localhost:8080/`.
111 |
112 | Start the local TeamCity server:
113 | ```
114 | docker-compose up -d
115 | ```
116 |
117 | Stop the local TeamCity server:
118 | ```
119 | docker-compose stop
120 | ```
121 |
122 | Remove the local TeamCity server containers:
123 | ```
124 | docker-compose down
125 | ```
126 |
--------------------------------------------------------------------------------
/tc-discord-webhooks-server/src/main/java/com/github/playerforcehd/tcdiscordwebhooks/discord/DiscordWebHookPayload.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2021 Pascal Zarrad
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.github.playerforcehd.tcdiscordwebhooks.discord;
18 |
19 | import com.github.playerforcehd.tcdiscordwebhooks.discord.embeds.DiscordEmbed;
20 |
21 | /**
22 | * A basic Discord payload that can be serialized into
23 | * JSON to be sent to Discord's WebHook API.
24 | *
25 | * The fields of this class represent the attributes of a
26 | * WebHook request as described in:
27 | * https://discordapp.com/developers/docs/resources/webhook
28 | *
29 | * @author Pascal Zarrad
30 | */
31 | public class DiscordWebHookPayload {
32 |
33 | /**
34 | * The username that will be used as the sender username
35 | */
36 | private String username;
37 |
38 | /**
39 | * The url to the avatar to use in the message
40 | */
41 | private String avatar_url;
42 |
43 | /**
44 | * Enable or disable the text-to-speech feature for this message
45 | */
46 | private boolean tts;
47 |
48 | /**
49 | * The content as a simple text message that should be send with the payload
50 | */
51 | private String content;
52 |
53 | /**
54 | * The embeds that should be send with the payload
55 | */
56 | private DiscordEmbed[] embeds;
57 |
58 | /**
59 | * Creates a new {@link DiscordWebHookPayload} that is prepared to send a simple text message bundled with an embed.
60 | *
61 | * @param username The username that will be used as the sender username
62 | * @param avatar_url The url to the avatar to use in the message
63 | * @param tts Enable or disable the text-to-speech feature for this message
64 | * @param content The content as a simple text message that should be send with the payload
65 | * @param embeds The embeds that should be send with the payload
66 | */
67 | public DiscordWebHookPayload(String username, String avatar_url, boolean tts, String content, DiscordEmbed[] embeds) {
68 | this.username = username;
69 | this.avatar_url = avatar_url;
70 | this.tts = tts;
71 | this.content = content;
72 | this.embeds = embeds;
73 | }
74 |
75 | /**
76 | * Creates a new {@link DiscordWebHookPayload} that is prepared to be send as a simple text message
77 | *
78 | * @param username The username that will be used as the sender username
79 | * @param avatar_url The url to the avatar to use in the message
80 | * @param tts Enable or disable the text-to-speech feature for this message
81 | * @param content The content as a simple text message that should be send with the payload
82 | */
83 | public DiscordWebHookPayload(String username, String avatar_url, boolean tts, String content) {
84 | this.username = username;
85 | this.avatar_url = avatar_url;
86 | this.tts = tts;
87 | this.content = content;
88 | }
89 |
90 | /**
91 | * Creates a new {@link DiscordWebHookPayload} that is prepared to be send bundled with {@link DiscordEmbed}'s
92 | *
93 | * @param username The username that will be used as the sender username
94 | * @param avatar_url The url to the avatar to use in the message
95 | * @param embeds The embeds that should be send with the payload
96 | */
97 | public DiscordWebHookPayload(String username, String avatar_url, DiscordEmbed[] embeds) {
98 | this.username = username;
99 | this.avatar_url = avatar_url;
100 | this.embeds = embeds;
101 | }
102 |
103 | public DiscordWebHookPayload() {
104 | }
105 |
106 | public String getUsername() {
107 | return username;
108 | }
109 |
110 | public void setUsername(String username) {
111 | this.username = username;
112 | }
113 |
114 | public String getAvatar_url() {
115 | return avatar_url;
116 | }
117 |
118 | public void setAvatar_url(String avatar_url) {
119 | this.avatar_url = avatar_url;
120 | }
121 |
122 | public boolean isTts() {
123 | return tts;
124 | }
125 |
126 | public void setTts(boolean tts) {
127 | this.tts = tts;
128 | }
129 |
130 | public String getContent() {
131 | return content;
132 | }
133 |
134 | public void setContent(String content) {
135 | this.content = content;
136 | }
137 |
138 | public DiscordEmbed[] getEmbeds() {
139 | return embeds;
140 | }
141 |
142 | public void setEmbeds(DiscordEmbed[] embeds) {
143 | this.embeds = embeds;
144 | }
145 | }
146 |
--------------------------------------------------------------------------------
/tc-discord-webhooks-server/src/main/java/com/github/playerforcehd/tcdiscordwebhooks/discord/embeds/DiscordEmbed.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2021 Pascal Zarrad
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.github.playerforcehd.tcdiscordwebhooks.discord.embeds;
18 |
19 | /**
20 | * A {@link DiscordEmbed} represents a Java object that contains every attribute
21 | * that can be applied to an embedded Discord message.
22 | *
23 | * NOTE: As embeds produced by this application are meant to be used for WebHooks,
24 | * the attribute type is always set to "rich".
25 | * Some attributes like video are not available because they are not
26 | * supported. So implementing them does not make any sense.
27 | *
28 | * The documentation about Discord's embeds can be found here:
29 | * https://discordapp.com/developers/docs/resources/webhook
30 | *
31 | * @author Pascal Zarrad
32 | */
33 | public class DiscordEmbed {
34 |
35 | /**
36 | * The title that of the embed
37 | */
38 | private String title;
39 | /**
40 | * The description of the embed
41 | */
42 | private String description;
43 |
44 | /**
45 | * The URL of the embed
46 | */
47 | private String url;
48 |
49 | /**
50 | * The color that the embed should have
51 | */
52 | private int color;
53 |
54 | /**
55 | * The footer to use for the embed
56 | */
57 | private DiscordEmbedFooter footer;
58 |
59 | /**
60 | * The image to use for the embed
61 | */
62 | private DiscordEmbedImage image;
63 |
64 | /**
65 | * The thumbnail to set for the embed.
66 | * NOTE: The thumbnail embed object has the same properties as
67 | * an image embed, so they share a class.
68 | */
69 | private DiscordEmbedImage thumbnail;
70 |
71 | /**
72 | * Contains additional fields of text for the embed
73 | */
74 | private DiscordEmbedField[] fields;
75 |
76 | public DiscordEmbed(String title, String description, String url, int color, DiscordEmbedFooter footer, DiscordEmbedImage image, DiscordEmbedImage thumbnail, DiscordEmbedField[] fields) {
77 | this.title = title;
78 | this.description = description;
79 | this.url = url;
80 | this.color = color;
81 | this.footer = footer;
82 | this.image = image;
83 | this.thumbnail = thumbnail;
84 | this.fields = fields;
85 | }
86 |
87 | public DiscordEmbed(String title, String description, String url, int color, DiscordEmbedFooter footer, DiscordEmbedImage thumbnail, DiscordEmbedField[] fields) {
88 | this.title = title;
89 | this.description = description;
90 | this.url = url;
91 | this.color = color;
92 | this.footer = footer;
93 | this.thumbnail = thumbnail;
94 | this.fields = fields;
95 | }
96 |
97 | public DiscordEmbed(String title, String description, String url, int color, DiscordEmbedFooter footer, DiscordEmbedField[] fields) {
98 | this.title = title;
99 | this.description = description;
100 | this.url = url;
101 | this.color = color;
102 | this.footer = footer;
103 | this.fields = fields;
104 | }
105 |
106 | public DiscordEmbed() {
107 | }
108 |
109 | public String getTitle() {
110 | return title;
111 | }
112 |
113 | public void setTitle(String title) {
114 | this.title = title;
115 | }
116 |
117 | public String getDescription() {
118 | return description;
119 | }
120 |
121 | public void setDescription(String description) {
122 | this.description = description;
123 | }
124 |
125 | public String getUrl() {
126 | return url;
127 | }
128 |
129 | public void setUrl(String url) {
130 | this.url = url;
131 | }
132 |
133 | public int getColor() {
134 | return color;
135 | }
136 |
137 | public void setColor(int color) {
138 | this.color = color;
139 | }
140 |
141 | public DiscordEmbedFooter getFooter() {
142 | return footer;
143 | }
144 |
145 | public void setFooter(DiscordEmbedFooter footer) {
146 | this.footer = footer;
147 | }
148 |
149 | public DiscordEmbedImage getImage() {
150 | return image;
151 | }
152 |
153 | public void setImage(DiscordEmbedImage image) {
154 | this.image = image;
155 | }
156 |
157 | public DiscordEmbedImage getThumbnail() {
158 | return thumbnail;
159 | }
160 |
161 | public void setThumbnail(DiscordEmbedImage thumbnail) {
162 | this.thumbnail = thumbnail;
163 | }
164 |
165 | public DiscordEmbedField[] getFields() {
166 | return fields;
167 | }
168 |
169 | public void setFields(DiscordEmbedField[] fields) {
170 | this.fields = fields;
171 | }
172 | }
173 |
--------------------------------------------------------------------------------
/tc-discord-webhooks-server/src/main/java/com/github/playerforcehd/tcdiscordwebhooks/discord/DiscordWebHookProcessor.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2021 Pascal Zarrad
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.github.playerforcehd.tcdiscordwebhooks.discord;
18 |
19 | import com.google.gson.Gson;
20 | import org.apache.http.HttpHost;
21 | import org.apache.http.client.config.RequestConfig;
22 | import org.apache.http.client.methods.CloseableHttpResponse;
23 | import org.apache.http.client.methods.HttpPost;
24 | import org.apache.http.entity.StringEntity;
25 | import org.apache.http.impl.client.CloseableHttpClient;
26 | import org.apache.http.impl.client.HttpClients;
27 |
28 | import java.io.IOException;
29 | import java.net.URISyntaxException;
30 | import java.net.URL;
31 | import java.nio.charset.StandardCharsets;
32 |
33 | /**
34 | * Handles the communication between the Discord WebHook API and the TeamCity server.
35 | * Also handles the serialization of the Discord WebHook Payloads
36 | *
37 | * @author Pascal Zarrad
38 | */
39 | public class DiscordWebHookProcessor {
40 |
41 | /**
42 | * The charset used for the requests
43 | */
44 | private static final String HTTP_CHARSET = StandardCharsets.UTF_8.toString();
45 |
46 | /**
47 | * The GSON instance used to serialize the {@link DiscordWebHookPayload}'s
48 | */
49 | private final Gson GSON;
50 |
51 | public DiscordWebHookProcessor() {
52 | this.GSON = new Gson();
53 | }
54 |
55 | /**
56 | * Send a WebHook request to the Discord API.
57 | * This method accepts a {@link DiscordWebHookPayload} as argument and serialises it before sending it.
58 | *
59 | * @param webHookURL The URL of the WebHook that is targeted
60 | * @param discordWebHookPayload The payload which contains the content to send
61 | * @return true if the request succeeded
62 | * @throws IOException Thrown when any I/O operation fails
63 | * @throws URISyntaxException Thrown when the given #webHookURL is invalid
64 | * @see DiscordWebHookProcessor#sendDiscordWebHook(String, String)
65 | */
66 | public boolean sendDiscordWebHook(String webHookURL, DiscordWebHookPayload discordWebHookPayload) throws IOException, URISyntaxException {
67 | return this.sendDiscordWebHook(webHookURL, this.serializeDiscordWebHookPayload(discordWebHookPayload));
68 | }
69 |
70 | /**
71 | * Send a WebHook request to the Discord API
72 | *
73 | * @param webHookURL The URL of the WebHook that is targeted
74 | * @param discordWebHookPayload The payload which contains the content to send
75 | * @return true if the request succeeded
76 | * @throws IOException Thrown when any I/O operation fails
77 | * @throws URISyntaxException Thrown when the given #webHookURL is invalid
78 | */
79 | public boolean sendDiscordWebHook(String webHookURL, String discordWebHookPayload) throws IOException, URISyntaxException {
80 | // Send Discord WebHook
81 | URL url = new URL(webHookURL);
82 | int responseCode; // We default to 400, when request succeeded, this should be 204
83 | try (CloseableHttpClient httpClient = HttpClients.createDefault()) {
84 | HttpPost httpPost = new HttpPost(url.toURI());
85 | httpPost.addHeader("User-Agent", "TeamCity Discord WebHook v1");
86 | httpPost.addHeader("Accept-Language", "en-US,en;q=0.5");
87 | httpPost.addHeader("Content-Type", "application/json");
88 | httpPost.setEntity(new StringEntity(discordWebHookPayload, HTTP_CHARSET));
89 | String httpProxyHost = System.getProperty("http.proxyHost");
90 | String httpProxyPort = System.getProperty("http.proxyPort");
91 | if (httpProxyHost != null && httpProxyPort.trim().length() > 0 && httpProxyPort != null) {
92 | int port = Integer.parseInt(httpProxyPort);
93 | HttpHost proxy = new HttpHost(httpProxyHost, port, "http");
94 | RequestConfig.Builder reqConfigBuilder = RequestConfig.custom();
95 | reqConfigBuilder = reqConfigBuilder.setProxy(proxy);
96 | RequestConfig config = reqConfigBuilder.build();
97 | httpPost.setConfig(config);
98 | }
99 | try (CloseableHttpResponse response = httpClient.execute(httpPost)) {
100 | responseCode = response.getStatusLine().getStatusCode();
101 | }
102 | }
103 | return responseCode == 204; // When request returned status 204, the request was a success
104 | }
105 |
106 | /**
107 | * Serializes a {@link DiscordWebHookPayload} into a JSON string.
108 | *
109 | * @param discordWebHookPayload The payload the serialize
110 | * @return The JSOn string of the {@link DiscordWebHookPayload}
111 | */
112 | public String serializeDiscordWebHookPayload(DiscordWebHookPayload discordWebHookPayload) {
113 | return this.GSON.toJson(discordWebHookPayload);
114 | }
115 |
116 | public Gson getGSON() {
117 | return GSON;
118 | }
119 | }
120 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | Thank you for your interest in contributing to TeamCity Discord Webhooks - but to keep everything organized, we need some guidelines and standards that apply to the project. We tried to keep those guidelines as short as possible and make it easy for new contributors to get started. Please read these guidelines carefully, to keep up with our issue, branching and pull request standards.
4 |
5 | ## Code of conduct
6 |
7 | Please refer to our code of conduct before you contribute anything as it sets some behavior rules to make a pleasant together. At the moment, when you contribute anything, you must follow this code of conduct.
8 | Dealing with questions, bugs and feature requests.
9 |
10 | If you have any questions, bugs or feature requests, feel free to open an issue. Note that you must use one of our issue templates that your issue is valid - valid issues will always retrieve the attention that is required to solve them. Before you create an issue, search for your subject and be sure to not create a duplicate. Always provide a clear and concise explanation of your matter of concern.
11 | Security issues.
12 |
13 | If you find some really serious security issue, please don't create a GitHub issue for it in the first line - it could put all users of TeamCity Discord Webhooks into great risk. Send a mail to p.zarrad@outlook.de instead, to keep it structured you can simply copy our bug template into your mail.
14 |
15 | ## Contributing code
16 |
17 | Code contributions are great because they allow anyone to contribute to their favorite projects - in addition they help us to make progress. Although there are some things to keep in mind when contributing code. You grant us a perpetual license to use your code under the MIT license at the moment you contribute something to this project. This contributing code section covers all defined standards that have nothing to do with or coding standards, as they are separated from our contribution guide - but we are recommending to read them, as code that not follows them will be rejected.
18 | First time contributors.
19 |
20 | As a first time contributor, you might find it hard to find an issue that you can work on. To make it easier for you, some issues will be labeled as "good first issue". This type of issue can be often done with a few changes to the source code, which does not require a too deep knowledge of the projects source code.
21 | Process of a code contribution.
22 |
23 | The step by step process for code contributions to TeamCity Discord Webhooks looks like the following:
24 |
25 | - Choose an issue that you want to resolve (Create one if you want to implement something, but there is no issue for it)
26 | - Create a comment in that issue that you want to resolve it.
27 | - You can now start to develop. Though the issue will not be yours till it has been assigned to you by a collaborator or maintainer.
28 | - If you're done with development, create a pull request.
29 | - Your PR will be reviewed by a collaborator or maintainer.
30 | - If your PR has been approved you're done, else start again at number 3 and make the required changes.
31 |
32 | ## Guidelines for issues
33 |
34 | The following things should be noted when working with issues:
35 |
36 | - Always use the matching template when you create new issues.
37 | - Keep the issue up to date with your current progress.
38 | - Do not create duplicates.
39 | - If you report a security issue, first visit our [security guidelines](SECURITY.md).
40 |
41 | If you don't follow these guidelines, your issue will be closed and labeled as invalid or duplicate.
42 |
43 | ## Pull requests, branching and commits
44 | ### Branching
45 |
46 | Our project uses a very standard branching model.
47 | We have the `master` branch that is always the most stable branch.
48 | A workflow deploys the latest push to the `master` branch to the Maven central repository.
49 | Pull requests to the `master` branch will only be accepted if they contain a version bump.
50 | The `develop` branch is our default branch.
51 | All contributions will be merged into `develop` using pull requests.
52 | A merge to the `master` branch triggers a snapshot release.
53 | If you want to contribute, fork the project and do your changes.
54 | You should create a feature or bug branch when contributing.
55 | We do not use documentation branches to minify the overhead.
56 |
57 | ### Commits
58 |
59 | Commit messages are great to express the intention of specific changes. In this project they are not only used
60 | to document changes but also to auto generate our changelogs.
61 | This means that some strict rules are necessary to ensure meaningful commit messages when contributing.
62 |
63 | This project uses some abbreviation of the [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/) standard.
64 |
65 | Your messages must have the following format:
66 | ```
67 | type: some imperative subject line
68 | ```
69 |
70 | If your commit contains breaking changes:
71 | ```
72 | type: some imperative subject line
73 |
74 | BREAKING CHANGE: Some breaking change
75 | ```
76 |
77 | Possible types:
78 | - feat: A new feature
79 | - fix: A bug fix
80 | - perf: A code change that improves performance
81 | - refactor: A code change that neither fixes a bug nor adds a feature
82 | - cs: Changes that do not affect the meaning of the code
83 | - test: Adding missing tests or correcting existing tests
84 | - build: Changes that affect the build system or external dependencies
85 | - ci: Changes to the CI configuration files and scripts
86 | - docs: Documentation only changes
87 | - changelog: Internally used by actions for commits that generate changelogs
88 | - bump: Bump version of the project
89 |
90 | Breaking changes should be noted using a "BREAKING CHANGE:" footer.
91 | The "!" notation must not be used to indicate a breaking change.
92 | You can also use concrete descriptions. You must put an empty line between the subject and footer.
93 | The subject after the double colon must start with a lower case letter.
94 | The subject line must be written in the imperative form (e.g. do this, make that, add something, ...).
95 |
96 | Scopes must not be used in commit messages, therefore no scopes have been defined.
97 |
98 | ### Pull requests
99 |
100 | When you're ready to create a pull request, use the predefined template for it. Choose a meaningful title (maybe the issues title will fit) and add all required labels. It is very important to link the right issue in the "Solves issue" section of the template, as every PR must be the result of an open issue.
101 |
102 | Requirements for an approved PR:
103 |
104 | - Merge into develop: You need one approving review from a collaborator or maintainer.
105 | - The TravisCI checks have to pass.
106 | - The commit messages must follow the guidelines mentioned in this document.
107 | - All requested changes and conversations have to be resolved before the merge.
108 | - You need a linked issue that the PR solves.
109 | - Merge into master: Only develop is allowed to be merged into master.
110 | - Merge into master: You need two approving reviews. One must be from a maintainer.
111 |
112 | ## You're ready
113 |
114 | As you read this contribution guidelines, the code of conduct, and most important our coding guidelines you are good to go! We really appreciate your work spent on contributions to our project, as people like you make the open source community great ❤️!
115 |
116 | If you have some suggestions to this contributing guidelines, feel free to suggest them!
117 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
20 | 4.0.0
21 |
22 | com.github.playerforcehd
23 | tc-discord-webhooks
24 | 1.1.1
25 | pom
26 |
27 | TC Discord WebHooks
28 | A TeamCity plugin that allows the easy creation of Discord WebHooks that are used to notify users on
29 | a Discord server about the current state of builds.
30 |
31 | https://github.com/pascal-zarrad/tc-discord-webhooks
32 |
33 |
34 | 2018.2
35 | UTF-8
36 |
37 |
38 |
39 |
40 | Apache-2.0 License
41 | https://www.apache.org/licenses/LICENSE-2.0
42 |
43 |
44 |
45 |
46 |
47 | Pascal Zarrad
48 | p.zarrad@outlook.de
49 | - None -
50 | https://pascal-zarrad.de/
51 |
52 |
53 |
54 |
55 | scm:git:git://github.com/pascal-zarrad/tc-discord-webhooks.git
56 | scm:git:ssh://github.com/pascal-zarrad/tc-discord-webhooks.git
57 | https://github.com/pascal-zarrad/tc-discord-webhooks/tree/master
58 |
59 |
60 |
61 |
62 | JetBrains
63 | https://download.jetbrains.com/teamcity-repository
64 |
65 |
66 |
67 |
68 | tc-discord-webhooks-server
69 | build
70 |
71 |
72 |
73 |
74 |
75 |
82 |
83 |
84 | org.apache.httpcomponents
85 | httpclient
86 | 4.5.6
87 | provided
88 |
89 |
90 |
91 | com.google.code.gson
92 | gson
93 | 2.4
94 | provided
95 |
96 |
97 |
98 |
99 | org.testng
100 | testng
101 | 6.8.21
102 | test
103 |
104 |
105 |
106 | com.github.tomakehurst
107 | wiremock-jre8
108 | 2.23.2
109 | test
110 |
111 |
112 |
113 |
114 |
115 | JetBrains
116 | https://download.jetbrains.com/teamcity-repository
117 |
118 |
119 |
120 |
121 |
122 |
123 | org.jacoco
124 | jacoco-maven-plugin
125 | 0.8.5
126 |
127 |
128 |
129 | prepare-agent
130 |
131 |
132 |
133 | report
134 | prepare-package
135 |
136 | report
137 |
138 |
139 |
140 |
141 |
142 | org.jetbrains.teamcity
143 | teamcity-sdk-maven-plugin
144 | 0.4
145 |
146 |
147 |
148 |
149 |
150 | org.apache.maven.plugins
151 | maven-compiler-plugin
152 | 3.8.1
153 |
154 | 1.8
155 | 1.8
156 |
157 |
158 |
159 | org.apache.maven.plugins
160 | maven-javadoc-plugin
161 | 3.0.1
162 |
163 |
164 | attach-javadocs
165 |
166 | jar
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 | org.jetbrains.teamcity
176 | teamcity-sdk-maven-plugin
177 | 0.4
178 |
179 |
180 |
181 |
182 |
183 | org.apache.maven.plugins
184 | maven-javadoc-plugin
185 | 3.0.1
186 |
187 | 8
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
--------------------------------------------------------------------------------
/tc-discord-webhooks-server/src/test/java/com/gtihub/playerforcehd/tcdiscordwebhooks/DiscordWebHookTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2021 Pascal Zarrad
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.gtihub.playerforcehd.tcdiscordwebhooks;
18 |
19 | import com.github.playerforcehd.tcdiscordwebhooks.discord.DiscordWebHookPayload;
20 | import com.github.playerforcehd.tcdiscordwebhooks.discord.DiscordWebHookProcessor;
21 | import com.github.playerforcehd.tcdiscordwebhooks.discord.embeds.*;
22 | import com.github.tomakehurst.wiremock.WireMockServer;
23 | import com.google.gson.JsonObject;
24 | import com.google.gson.JsonParser;
25 | import org.testng.annotations.AfterMethod;
26 | import org.testng.annotations.BeforeMethod;
27 | import org.testng.annotations.Test;
28 |
29 | import java.io.IOException;
30 | import java.net.URISyntaxException;
31 |
32 | import static com.github.tomakehurst.wiremock.client.WireMock.*;
33 | import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
34 | import static org.testng.Assert.*;
35 |
36 | /**
37 | * Tests to check if the DiscordWebHookPayload's are working properly.
38 | *
39 | * @author Pascal Zarrad
40 | */
41 | public class DiscordWebHookTest {
42 |
43 | /**
44 | * An example payload that has been validated to work properly.
45 | * It uses every supported feature, so the serialization and sending of WebHooks can
46 | * be tested full scope.
47 | */
48 | private final String exampleWebHookPayload = "{\n" +
49 | " \"username\": \"Test User\",\n" +
50 | " \"avatar_url\": \"http://localhost/myAvatar.png\",\n" +
51 | " \"tts\": true,\n" +
52 | " \"content\": \"This is great content!\",\n" +
53 | " \"embeds\": [\n" +
54 | " {\n" +
55 | " \"title\": \"Embed Title!\",\n" +
56 | " \"description\": \"My Description\",\n" +
57 | " \"url\": \"https://discordapp.com/\",\n" +
58 | " \"color\": 16711680,\n" +
59 | " \"footer\": {\n" +
60 | " \"text\": \"Test Text\",\n" +
61 | " \"icon_url\": \"http://localhost/footerIcon.png\"\n" +
62 | " },\n" +
63 | " \"image\": {\n" +
64 | " \"url\": \"http://localhost/EmbeddedImage.png\"\n" +
65 | " },\n" +
66 | " \"thumbnail\": {\n" +
67 | " \"url\": \"http://localhost/thumbnail.png\"\n" +
68 | " },\n" +
69 | " \"fields\": [\n" +
70 | " {\n" +
71 | " \"name\": \"An embedded field\",\n" +
72 | " \"value\": \"This is an embedded field!\",\n" +
73 | " \"inline\": false\n" +
74 | " }\n" +
75 | " ]\n" +
76 | " }\n" +
77 | " ]\n" +
78 | "}";
79 |
80 | /**
81 | * The {@link WireMockServer}used to mock a WebServer to test the execution of WebHooks
82 | */
83 | private WireMockServer wireMockServer;
84 |
85 | /**
86 | * Prepares a {@link WireMockServer} to provide support for test relying on a WebServer.
87 | */
88 | @BeforeMethod
89 | public void prepare() {
90 | this.wireMockServer = new WireMockServer(options().dynamicPort().bindAddress("127.0.0.1"));
91 | this.wireMockServer.start();
92 | }
93 |
94 | /**
95 | * Stops the running {@link WireMockServer}
96 | */
97 | @AfterMethod
98 | public void reset() {
99 | this.wireMockServer.stop();
100 | }
101 |
102 | /**
103 | * Tests if the values that are passed to a DiscordWebHookPayload are assigned to the right fields.
104 | */
105 | @Test
106 | public void testSerializationOfDiscordWebHookPayloadEqualsReferencePayloadJSON() {
107 | // Create a DiscordWebHookPayload
108 | DiscordWebHookPayload discordWebHookPayload = new DiscordWebHookPayload("Test User", "http://localhost/myAvatar.png", true, "This is great content!", new DiscordEmbed[]{
109 | new DiscordEmbed("Embed Title!", "My Description", "https://discordapp.com/", DiscordEmbedColor.RED,
110 | new DiscordEmbedFooter("Test Text", "http://localhost/footerIcon.png"),
111 | new DiscordEmbedImage("http://localhost/EmbeddedImage.png"),
112 | new DiscordEmbedImage("http://localhost/thumbnail.png"),
113 | new DiscordEmbedField[]{
114 | new DiscordEmbedField("An embedded field", "This is an embedded field!")
115 | })
116 | });
117 | // Create DiscordWebHookProcessor
118 | DiscordWebHookProcessor discordWebHookProcessor = new DiscordWebHookProcessor();
119 | // Serialize the DiscordWebHookPayload into a JSON string
120 | String discordWebHookPayloadJSON = discordWebHookProcessor.serializeDiscordWebHookPayload(discordWebHookPayload);
121 | // Parse our created payload
122 | JsonParser parser = new JsonParser();
123 | JsonObject payloadElement = (JsonObject) parser.parse(discordWebHookPayloadJSON);
124 | // Parse the reference sting
125 | JsonObject referenceElement = (JsonObject) parser.parse(this.exampleWebHookPayload);
126 | // Now the two JSonElements should have the same content
127 | assertEquals(payloadElement, referenceElement, "The serialized payload element does not equal to the reference element!");
128 | }
129 |
130 | /**
131 | * Test if the {@link DiscordWebHookProcessor#sendDiscordWebHook(String, String)} sends a valid WebHook request
132 | */
133 | @Test
134 | public void testValidDiscordWebHook() {
135 | // Create a DiscordWebHookPayload
136 | DiscordWebHookPayload discordWebHookPayload = new DiscordWebHookPayload("Test User", "http://localhost/myAvatar.png", false, "This is great content!", new DiscordEmbed[]{
137 | new DiscordEmbed("Embed Title!", "My Description", "https://discordapp.com/", DiscordEmbedColor.RED,
138 | new DiscordEmbedFooter("Test Text", "http://localhost/footerIcon.png"),
139 | new DiscordEmbedImage("http://localhost/EmbeddedImage.png"),
140 | new DiscordEmbedImage("http://localhost/thumbnail.png"),
141 | new DiscordEmbedField[]{
142 | new DiscordEmbedField("An embedded field", "This is an embedded field!")
143 | })
144 | });
145 | // Create DiscordWebHookProcessor
146 | DiscordWebHookProcessor discordWebHookProcessor = new DiscordWebHookProcessor();
147 | String discordWebHookContent = discordWebHookProcessor.serializeDiscordWebHookPayload(discordWebHookPayload);
148 | // Configure WireMock
149 | String localRequestUrl = "http://" + this.wireMockServer.getOptions().bindAddress() + ":" + this.wireMockServer.port() + "/anyWebHookID/anyWebHookToken";
150 | this.wireMockServer.stubFor(any(urlPathEqualTo("/anyWebHookID/anyWebHookToken"))
151 | .withHeader("User-Agent", equalTo("TeamCity Discord WebHook v1"))
152 | .withHeader("Accept-Language", equalTo("en-US,en;q=0.5"))
153 | .withHeader("Content-Type", equalTo("application/json"))
154 | .withRequestBody(equalToJson(discordWebHookContent))
155 | .willReturn(noContent())
156 | );
157 | // Send WebHook
158 | try {
159 | assertTrue(discordWebHookProcessor.sendDiscordWebHook(localRequestUrl, discordWebHookPayload), "The send request should end up in Error 204 (No content) and trigger a return true, which represents a valid request!");
160 | } catch (IOException | URISyntaxException e) {
161 | fail("Failed to send Discord WebHook due to an IOException!", e);
162 | }
163 | }
164 |
165 | /**
166 | * Test if the {@link DiscordWebHookProcessor#sendDiscordWebHook(String, String)} fails when sending an invalid request
167 | */
168 | @Test
169 | public void testValidDiscordWebHookFailsForInvalidContent() {
170 | // Create a DiscordWebHookPayload
171 | DiscordWebHookPayload discordWebHookPayload = new DiscordWebHookPayload(null, null, false, null);
172 | // Create DiscordWebHookProcessor
173 | DiscordWebHookProcessor discordWebHookProcessor = new DiscordWebHookProcessor();
174 | String discordWebHookContent = discordWebHookProcessor.serializeDiscordWebHookPayload(discordWebHookPayload);
175 | // Configure WireMock
176 | String localRequestUrl = "http://" + this.wireMockServer.getOptions().bindAddress() + ":" + this.wireMockServer.port() + "/anyWebHookID/anyWebHookToken";
177 | this.wireMockServer.stubFor(any(urlPathEqualTo("/anyWebHookID/anyWebHookToken"))
178 | .withHeader("User-Agent", equalTo("TeamCity Discord WebHook v1"))
179 | .withHeader("Accept-Language", equalTo("en-US,en;q=0.5"))
180 | .withHeader("Content-Type", equalTo("application/json"))
181 | .withRequestBody(equalToJson(discordWebHookContent)) // Using the example payload content to check here.
182 | .willReturn(badRequest())
183 | );
184 | // Send WebHook
185 | try {
186 | assertFalse(discordWebHookProcessor.sendDiscordWebHook(localRequestUrl, discordWebHookPayload), "The WebServer accepted the request, but the content did not equal a valid format or had valid content!");
187 | } catch (IOException | URISyntaxException e) {
188 | fail("Failed to send Discord WebHook due to an IOException!", e);
189 | }
190 | }
191 | }
192 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/tc-discord-webhooks-server/src/main/java/com/github/playerforcehd/tcdiscordwebhooks/notificator/DiscordNotificator.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2021 Pascal Zarrad
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package com.github.playerforcehd.tcdiscordwebhooks.notificator;
18 |
19 | import com.github.playerforcehd.tcdiscordwebhooks.discord.DiscordWebHookPayload;
20 | import com.github.playerforcehd.tcdiscordwebhooks.discord.DiscordWebHookProcessor;
21 | import com.github.playerforcehd.tcdiscordwebhooks.discord.embeds.DiscordEmbed;
22 | import com.github.playerforcehd.tcdiscordwebhooks.discord.embeds.DiscordEmbedColor;
23 | import com.github.playerforcehd.tcdiscordwebhooks.discord.embeds.DiscordEmbedField;
24 | import jetbrains.buildServer.Build;
25 | import jetbrains.buildServer.notification.Notificator;
26 | import jetbrains.buildServer.notification.NotificatorRegistry;
27 | import jetbrains.buildServer.responsibility.ResponsibilityEntry;
28 | import jetbrains.buildServer.responsibility.TestNameResponsibilityEntry;
29 | import jetbrains.buildServer.serverSide.*;
30 | import jetbrains.buildServer.serverSide.comments.Comment;
31 | import jetbrains.buildServer.serverSide.mute.MuteInfo;
32 | import jetbrains.buildServer.serverSide.problems.BuildProblemInfo;
33 | import jetbrains.buildServer.tests.TestName;
34 | import jetbrains.buildServer.users.NotificatorPropertyKey;
35 | import jetbrains.buildServer.users.PropertyKey;
36 | import jetbrains.buildServer.users.SUser;
37 | import jetbrains.buildServer.vcs.VcsRoot;
38 | import org.apache.log4j.Logger;
39 | import org.jetbrains.annotations.NotNull;
40 | import org.jetbrains.annotations.Nullable;
41 |
42 | import java.io.IOException;
43 | import java.net.URISyntaxException;
44 | import java.util.ArrayList;
45 | import java.util.Collection;
46 | import java.util.List;
47 | import java.util.Set;
48 |
49 | /**
50 | * The {@link Notificator} service that handles triggered notifications
51 | *
52 | * @author Pascal Zarrad
53 | */
54 | public class DiscordNotificator implements Notificator {
55 |
56 | /**
57 | * The logger used for debug messages.
58 | * Mostly used for error logging.
59 | */
60 | private static final Logger LOGGER = Logger.getLogger(DiscordNotificator.class);
61 | /**
62 | * The type of this {@link Notificator}
63 | */
64 | private static final String TYPE = "DiscordNotificator";
65 | /**
66 | * The display name of this notificator
67 | */
68 | private static final String DISPLAY_NAME = "Discord WebHook";
69 | /**
70 | * The name of the {@link PropertyKey} {@link #WEBHOOK_URL}
71 | */
72 | private static final String WEBHOOK_URL_KEY = "DiscordWebHookURL";
73 | /**
74 | * The name of the {@link PropertyKey} {@link #USERNAME}
75 | */
76 | private static final String WEBHOOK_USERNAME_KEY = "DiscordUsername";
77 | /**
78 | * {@link PropertyKey} of the property that defines the URL of the WebHook
79 | */
80 | private static final PropertyKey WEBHOOK_URL = new NotificatorPropertyKey(TYPE, WEBHOOK_URL_KEY);
81 | /**
82 | * {@link PropertyKey} of the property that defines the Username of the WebHook
83 | */
84 | private static final PropertyKey USERNAME = new NotificatorPropertyKey(TYPE, WEBHOOK_USERNAME_KEY);
85 | /**
86 | * The string used for situation where no data is available to display
87 | */
88 | private static final String NO_DATA = "";
89 | /**
90 | * The {@link DiscordWebHookProcessor} that is used to trigger the WebHooks
91 | */
92 | private final DiscordWebHookProcessor discordWebHookProcessor;
93 |
94 | /**
95 | * The {@link SBuildServer} this {@link Notificator} belongs to
96 | */
97 | private SBuildServer sBuildServer;
98 |
99 | public DiscordNotificator(NotificatorRegistry notificatorRegistry, SBuildServer sBuildServer) {
100 | this.discordWebHookProcessor = new DiscordWebHookProcessor();
101 | this.sBuildServer = sBuildServer;
102 | this.initializeNotificator(notificatorRegistry);
103 | }
104 |
105 | /**
106 | * Creates all the {@link UserPropertyInfo}'s and registers this {@link Notificator}
107 | *
108 | * @param notificatorRegistry The {@link NotificatorRegistry} where this {@link Notificator} will be registered to
109 | */
110 | private void initializeNotificator(NotificatorRegistry notificatorRegistry) {
111 | ArrayList userProperties = new ArrayList<>();
112 | userProperties.add(new UserPropertyInfo(WEBHOOK_URL_KEY, "WebHook URL"));
113 | userProperties.add(new UserPropertyInfo(WEBHOOK_USERNAME_KEY, "Username"));
114 | notificatorRegistry.register(this, userProperties);
115 | }
116 |
117 | /**
118 | * Send the notification by triggering the
119 | * {@link DiscordWebHookProcessor#sendDiscordWebHook(String, DiscordWebHookPayload)}
120 | * method using the data given in the discordWebHookPayload parameter.
121 | *
122 | * @param discordWebHookPayload The payload to send
123 | * @param users The users that should be notified
124 | */
125 | private void processNotify(@NotNull DiscordWebHookPayload discordWebHookPayload, @NotNull Set users) {
126 | for (SUser user : users) {
127 | String webHookUrl = user.getPropertyValue(WEBHOOK_URL);
128 | String username = user.getPropertyValue(USERNAME);
129 | if (webHookUrl == null || webHookUrl.equals("")) {
130 | LOGGER.error("The Discord WebHook URL for user '" + user.getName() + "' has not been set. Can't execute the WebHook!");
131 | return;
132 | }
133 | if (username != null && !username.equals("")) {
134 | discordWebHookPayload.setUsername(username);
135 | }
136 | try {
137 | this.discordWebHookProcessor.sendDiscordWebHook(webHookUrl, discordWebHookPayload);
138 | } catch (IOException | URISyntaxException e) {
139 | LOGGER.error("Failed to send the WebHook!", e);
140 | }
141 | }
142 | }
143 |
144 | /**
145 | * Gets a Project from a {@link SRunningBuild} by searching for a project
146 | * that has the same project id as the running build.
147 | *
148 | * @param sRunningBuild The {@link SRunningBuild} from which the Project should be grabbed
149 | * @return The project that owns the build or null
150 | */
151 | private SProject getProjectFromRunningBuild(SRunningBuild sRunningBuild) {
152 | for (SProject project : this.sBuildServer.getProjectManager().getProjects()) {
153 | if (project.getProjectId().equals(sRunningBuild.getProjectId())) {
154 | return project;
155 | }
156 | }
157 | return null;
158 | }
159 |
160 | /**
161 | * Builds the {@link DiscordEmbedField}'s that are used in all notifications
162 | * that are based on an {@link SRunningBuild}.
163 | *
164 | * @param sRunningBuild The build from which the fields will be build
165 | * @return The {@link DiscordEmbedField}'s created from the {@link SRunningBuild}
166 | */
167 | private DiscordEmbedField[] buildFieldsForRunningBuild(SRunningBuild sRunningBuild) {
168 | List discordEmbedFields = new ArrayList<>();
169 | // Grab data
170 | // Project
171 | SProject project = getProjectFromRunningBuild(sRunningBuild);
172 | String projectName = NO_DATA;
173 | if (project != null) {
174 | projectName = project.getName();
175 | }
176 | discordEmbedFields.add(new DiscordEmbedField("Project: ", projectName, true));
177 | // Build name
178 | discordEmbedFields.add(new DiscordEmbedField("Build:", sRunningBuild.getBuildTypeName(), true));
179 | // Branch
180 | Branch branch = sRunningBuild.getBranch();
181 | String branchName = "Default";
182 | if (branch != null && !branch.getName().equals(Branch.DEFAULT_BRANCH_NAME)) {
183 | branchName = branch.getDisplayName();
184 | }
185 | discordEmbedFields.add(new DiscordEmbedField("Branch", branchName, true));
186 | Comment comment = sRunningBuild.getBuildComment();
187 | if(comment != null) {
188 | discordEmbedFields.add(new DiscordEmbedField("Comment", comment.getComment(), false));
189 | }
190 | return discordEmbedFields.toArray(new DiscordEmbedField[0]);
191 | }
192 |
193 | @Override
194 | public void notifyBuildStarted(@NotNull SRunningBuild sRunningBuild, @NotNull Set users) {
195 | String title = "Build started";
196 | String description = "A build with the ID " + sRunningBuild.getBuildId() + " has been started!";
197 | String url = this.sBuildServer.getRootUrl() + "/viewLog.html?buildId=" + sRunningBuild.getBuildId();
198 | DiscordWebHookPayload discordWebHookPayload = new DiscordWebHookPayload();
199 | discordWebHookPayload.setEmbeds(new DiscordEmbed[]{
200 | new DiscordEmbed(
201 | title,
202 | description,
203 | url,
204 | DiscordEmbedColor.BLUE,
205 | null,
206 | null,
207 | null,
208 | buildFieldsForRunningBuild(sRunningBuild)
209 | )
210 | });
211 | this.processNotify(discordWebHookPayload, users);
212 | }
213 |
214 | @Override
215 | public void notifyBuildSuccessful(@NotNull SRunningBuild sRunningBuild, @NotNull Set users) {
216 | String title = "Build succeeded!";
217 | String description = "The build with the ID " + sRunningBuild.getBuildId() + " has succeeded!";
218 | String url = this.sBuildServer.getRootUrl() + "/viewLog.html?buildId=" + sRunningBuild.getBuildId();
219 | DiscordWebHookPayload discordWebHookPayload = new DiscordWebHookPayload();
220 | discordWebHookPayload.setEmbeds(new DiscordEmbed[]{
221 | new DiscordEmbed(
222 | title,
223 | description,
224 | url,
225 | DiscordEmbedColor.GREEN,
226 | null,
227 | null,
228 | null,
229 | buildFieldsForRunningBuild(sRunningBuild)
230 | )
231 | });
232 | this.processNotify(discordWebHookPayload, users);
233 | }
234 |
235 | @Override
236 | public void notifyBuildFailed(@NotNull SRunningBuild sRunningBuild, @NotNull Set users) {
237 | String title = "Build failed";
238 | String description = "The build with the ID " + sRunningBuild.getBuildId() + " has failed!";
239 | String url = this.sBuildServer.getRootUrl() + "/viewLog.html?buildId=" + sRunningBuild.getBuildId();
240 | DiscordWebHookPayload discordWebHookPayload = new DiscordWebHookPayload();
241 | discordWebHookPayload.setEmbeds(new DiscordEmbed[]{
242 | new DiscordEmbed(
243 | title,
244 | description,
245 | url,
246 | DiscordEmbedColor.RED,
247 | null,
248 | null,
249 | null,
250 | buildFieldsForRunningBuild(sRunningBuild)
251 | )
252 | });
253 | this.processNotify(discordWebHookPayload, users);
254 | }
255 |
256 | @Override
257 | public void notifyBuildFailedToStart(@NotNull SRunningBuild sRunningBuild, @NotNull Set users) {
258 | String title = "Build failed to start";
259 | String description = "The build with the ID " + sRunningBuild.getBuildId() + " has failed to start!";
260 | String url = this.sBuildServer.getRootUrl() + "/viewLog.html?buildId=" + sRunningBuild.getBuildId();
261 | DiscordWebHookPayload discordWebHookPayload = new DiscordWebHookPayload();
262 | discordWebHookPayload.setEmbeds(new DiscordEmbed[]{
263 | new DiscordEmbed(
264 | title,
265 | description,
266 | url,
267 | DiscordEmbedColor.RED,
268 | null,
269 | null,
270 | null,
271 | buildFieldsForRunningBuild(sRunningBuild)
272 | )
273 | });
274 | this.processNotify(discordWebHookPayload, users);
275 | }
276 |
277 | @Override
278 | public void notifyLabelingFailed(@NotNull Build build, @NotNull VcsRoot vcsRoot, @NotNull Throwable throwable, @NotNull Set users) {
279 | String title = "Labeling failed";
280 | String description = "Labeling of build with the ID " + build.getBuildId() + " has failed!";
281 | String url = this.sBuildServer.getRootUrl() + "/viewLog.html?buildId=" + build.getBuildId();
282 | DiscordWebHookPayload discordWebHookPayload = new DiscordWebHookPayload();
283 | discordWebHookPayload.setEmbeds(new DiscordEmbed[]{
284 | new DiscordEmbed(
285 | title,
286 | description,
287 | url,
288 | DiscordEmbedColor.RED,
289 | null,
290 | null,
291 | null,
292 | new DiscordEmbedField[]{}
293 | )
294 | });
295 | this.processNotify(discordWebHookPayload, users);
296 | }
297 |
298 | @Override
299 | public void notifyBuildFailing(@NotNull SRunningBuild sRunningBuild, @NotNull Set users) {
300 | String title = "Build is failing";
301 | String description = "The build with the ID " + sRunningBuild.getBuildId() + " is failing!";
302 | String url = this.sBuildServer.getRootUrl() + "/viewLog.html?buildId=" + sRunningBuild.getBuildId();
303 | DiscordWebHookPayload discordWebHookPayload = new DiscordWebHookPayload();
304 | discordWebHookPayload.setEmbeds(new DiscordEmbed[]{
305 | new DiscordEmbed(
306 | title,
307 | description,
308 | url,
309 | DiscordEmbedColor.RED,
310 | null,
311 | null,
312 | null,
313 | buildFieldsForRunningBuild(sRunningBuild)
314 | )
315 | });
316 | this.processNotify(discordWebHookPayload, users);
317 | }
318 |
319 | @Override
320 | public void notifyBuildProbablyHanging(@NotNull SRunningBuild sRunningBuild, @NotNull Set users) {
321 | String title = "Build is probably hanging";
322 | String description = "The build with the ID " + sRunningBuild.getBuildId() + " is probably hanging!";
323 | String url = this.sBuildServer.getRootUrl() + "/viewLog.html?buildId=" + sRunningBuild.getBuildId();
324 | DiscordWebHookPayload discordWebHookPayload = new DiscordWebHookPayload();
325 | discordWebHookPayload.setEmbeds(new DiscordEmbed[]{
326 | new DiscordEmbed(
327 | title,
328 | description,
329 | url,
330 | DiscordEmbedColor.ORANGE,
331 | null,
332 | null,
333 | null,
334 | buildFieldsForRunningBuild(sRunningBuild)
335 | )
336 | });
337 | this.processNotify(discordWebHookPayload, users);
338 | }
339 |
340 | @Override
341 | public void notifyResponsibleChanged(@NotNull SBuildType sBuildType, @NotNull Set users) {
342 | String title = "Responsibility for build type has changed";
343 | String description = "The responsibility for the build type " + sBuildType.getExtendedFullName() + " has changed!";
344 | DiscordWebHookPayload discordWebHookPayload = new DiscordWebHookPayload();
345 | discordWebHookPayload.setEmbeds(new DiscordEmbed[]{
346 | new DiscordEmbed(
347 | title,
348 | description,
349 | "",
350 | DiscordEmbedColor.ORANGE,
351 | null,
352 | null,
353 | null,
354 | new DiscordEmbedField[]{}
355 | )
356 | });
357 | this.processNotify(discordWebHookPayload, users);
358 | }
359 |
360 | @Override
361 | public void notifyResponsibleAssigned(@NotNull SBuildType sBuildType, @NotNull Set users) {
362 | String title = "Responsibility assigned";
363 | String description = "Responsibility for build type " + sBuildType.getExtendedFullName() + " has been assigned!";
364 | DiscordWebHookPayload discordWebHookPayload = new DiscordWebHookPayload();
365 | discordWebHookPayload.setEmbeds(new DiscordEmbed[]{
366 | new DiscordEmbed(
367 | title,
368 | description,
369 | "",
370 | DiscordEmbedColor.ORANGE,
371 | null,
372 | null,
373 | null,
374 | new DiscordEmbedField[]{}
375 | )
376 | });
377 | this.processNotify(discordWebHookPayload, users);
378 | }
379 |
380 | @Override
381 | public void notifyResponsibleChanged(@Nullable TestNameResponsibilityEntry testNameResponsibilityEntry, @NotNull TestNameResponsibilityEntry testNameResponsibilityEntry1, @NotNull SProject sProject, @NotNull Set users) {
382 | String title = "Responsibility changed";
383 | String description = "Responsibility for the project " + sProject.getFullName() + " has changed!";
384 | DiscordWebHookPayload discordWebHookPayload = new DiscordWebHookPayload();
385 | discordWebHookPayload.setEmbeds(new DiscordEmbed[]{
386 | new DiscordEmbed(
387 | title,
388 | description,
389 | "",
390 | DiscordEmbedColor.ORANGE,
391 | null,
392 | null,
393 | null,
394 | new DiscordEmbedField[]{}
395 | )
396 | });
397 | this.processNotify(discordWebHookPayload, users);
398 | }
399 |
400 | @Override
401 | public void notifyResponsibleAssigned(@Nullable TestNameResponsibilityEntry testNameResponsibilityEntry, @NotNull TestNameResponsibilityEntry testNameResponsibilityEntry1, @NotNull SProject sProject, @NotNull Set users) {
402 | String title = "Responsibility assigned";
403 | String description = "Responsibility for project " + sProject.getFullName() + " has been assigned!";
404 | DiscordWebHookPayload discordWebHookPayload = new DiscordWebHookPayload();
405 | discordWebHookPayload.setEmbeds(new DiscordEmbed[]{
406 | new DiscordEmbed(
407 | title,
408 | description,
409 | "",
410 | DiscordEmbedColor.ORANGE,
411 | null,
412 | null,
413 | null,
414 | new DiscordEmbedField[]{}
415 | )
416 | });
417 | this.processNotify(discordWebHookPayload, users);
418 | }
419 |
420 | @Override
421 | public void notifyResponsibleChanged(@NotNull Collection collection, @NotNull ResponsibilityEntry responsibilityEntry, @NotNull SProject sProject, @NotNull Set users) {
422 | String title = "Responsibility changed";
423 | String description = "Responsibility for project " + sProject.getFullName() + " has been changed!";
424 | DiscordWebHookPayload discordWebHookPayload = new DiscordWebHookPayload();
425 | discordWebHookPayload.setEmbeds(new DiscordEmbed[]{
426 | new DiscordEmbed(
427 | title,
428 | description,
429 | "",
430 | DiscordEmbedColor.ORANGE,
431 | null,
432 | null,
433 | null,
434 | new DiscordEmbedField[]{}
435 | )
436 | });
437 | this.processNotify(discordWebHookPayload, users);
438 | }
439 |
440 | @Override
441 | public void notifyResponsibleAssigned(@NotNull Collection collection, @NotNull ResponsibilityEntry responsibilityEntry, @NotNull SProject sProject, @NotNull Set users) {
442 | String title = "Responsibility assigned";
443 | String description = "Responsibility for one or more tests of project " + sProject.getFullName() + " have been assigned!";
444 | DiscordWebHookPayload discordWebHookPayload = new DiscordWebHookPayload();
445 | discordWebHookPayload.setEmbeds(new DiscordEmbed[]{
446 | new DiscordEmbed(
447 | title,
448 | description,
449 | "",
450 | DiscordEmbedColor.ORANGE,
451 | null,
452 | null,
453 | null,
454 | new DiscordEmbedField[]{}
455 | )
456 | });
457 | this.processNotify(discordWebHookPayload, users);
458 | }
459 |
460 | @Override
461 | public void notifyBuildProblemResponsibleAssigned(@NotNull Collection collection, @NotNull ResponsibilityEntry responsibilityEntry, @NotNull SProject sProject, @NotNull Set users) {
462 | String title = "Responsibility assigned";
463 | String description = "Responsibility for one or more build problems of project " + sProject.getFullName() + " have been assigned!";
464 | DiscordWebHookPayload discordWebHookPayload = new DiscordWebHookPayload();
465 | discordWebHookPayload.setEmbeds(new DiscordEmbed[]{
466 | new DiscordEmbed(
467 | title,
468 | description,
469 | "",
470 | DiscordEmbedColor.ORANGE,
471 | null,
472 | null,
473 | null,
474 | new DiscordEmbedField[]{}
475 | )
476 | });
477 | this.processNotify(discordWebHookPayload, users);
478 | }
479 |
480 | @Override
481 | public void notifyBuildProblemResponsibleChanged(@NotNull Collection collection, @NotNull ResponsibilityEntry responsibilityEntry, @NotNull SProject sProject, @NotNull Set users) {
482 | String title = "Responsibility assigned";
483 | String description = "Responsibility for one or more tests of project " + sProject.getFullName() + " has been changed!";
484 | DiscordWebHookPayload discordWebHookPayload = new DiscordWebHookPayload();
485 | discordWebHookPayload.setEmbeds(new DiscordEmbed[]{
486 | new DiscordEmbed(
487 | title,
488 | description,
489 | "",
490 | DiscordEmbedColor.ORANGE,
491 | null,
492 | null,
493 | null,
494 | new DiscordEmbedField[]{}
495 | )
496 | });
497 | this.processNotify(discordWebHookPayload, users);
498 | }
499 |
500 | @Override
501 | public void notifyTestsMuted(@NotNull Collection collection, @NotNull MuteInfo muteInfo, @NotNull Set users) {
502 | String title = "Tests muted";
503 | if (muteInfo.getProject() != null) {
504 | muteInfo.getProject().getFullName();
505 | String description = "One or more tests of the project " + muteInfo.getProject().getFullName() + " have been muted!";
506 | DiscordWebHookPayload discordWebHookPayload = new DiscordWebHookPayload();
507 | discordWebHookPayload.setEmbeds(new DiscordEmbed[]{
508 | new DiscordEmbed(
509 | title,
510 | description,
511 | "",
512 | DiscordEmbedColor.ORANGE,
513 | null,
514 | null,
515 | null,
516 | new DiscordEmbedField[]{}
517 | )
518 | });
519 | this.processNotify(discordWebHookPayload, users);
520 | }
521 | }
522 |
523 | @Override
524 | public void notifyTestsUnmuted(@NotNull Collection collection, @NotNull MuteInfo muteInfo, @Nullable SUser sUser, @NotNull Set users) {
525 | String title = "Tests unmuted";
526 | if (muteInfo.getProject() != null) {
527 | muteInfo.getProject().getFullName();
528 | String description = "One or more tests of the project " + muteInfo.getProject().getFullName() + " have been unmuted!";
529 | DiscordWebHookPayload discordWebHookPayload = new DiscordWebHookPayload();
530 | discordWebHookPayload.setEmbeds(new DiscordEmbed[]{
531 | new DiscordEmbed(
532 | title,
533 | description,
534 | "",
535 | DiscordEmbedColor.ORANGE,
536 | null,
537 | null,
538 | null,
539 | new DiscordEmbedField[]{}
540 | )
541 | });
542 | this.processNotify(discordWebHookPayload, users);
543 | }
544 | }
545 |
546 | @Override
547 | public void notifyBuildProblemsMuted(@NotNull Collection collection, @NotNull MuteInfo muteInfo, @NotNull Set users) {
548 | String title = "Build problems muted";
549 | if (muteInfo.getProject() != null) {
550 | muteInfo.getProject().getFullName();
551 | String description = "One or more build problems of the project " + muteInfo.getProject().getFullName() + " have been muted!";
552 | DiscordWebHookPayload discordWebHookPayload = new DiscordWebHookPayload();
553 | discordWebHookPayload.setEmbeds(new DiscordEmbed[]{
554 | new DiscordEmbed(
555 | title,
556 | description,
557 | "",
558 | DiscordEmbedColor.ORANGE,
559 | null,
560 | null,
561 | null,
562 | new DiscordEmbedField[]{}
563 | )
564 | });
565 | this.processNotify(discordWebHookPayload, users);
566 | }
567 | }
568 |
569 | @Override
570 | public void notifyBuildProblemsUnmuted(@NotNull Collection collection, @NotNull MuteInfo muteInfo, @Nullable SUser sUser, @NotNull Set users) {
571 | String title = "Build problems unmuted";
572 | if (muteInfo.getProject() != null) {
573 | muteInfo.getProject().getFullName();
574 | String description = "One or more build problems of the project " + muteInfo.getProject().getFullName() + " have been unmuted!";
575 | DiscordWebHookPayload discordWebHookPayload = new DiscordWebHookPayload();
576 | discordWebHookPayload.setEmbeds(new DiscordEmbed[]{
577 | new DiscordEmbed(
578 | title,
579 | description,
580 | "",
581 | DiscordEmbedColor.ORANGE,
582 | null,
583 | null,
584 | null,
585 | new DiscordEmbedField[]{}
586 | )
587 | });
588 | this.processNotify(discordWebHookPayload, users);
589 | }
590 | }
591 |
592 | @NotNull
593 | @Override
594 | public String getNotificatorType() {
595 | return TYPE;
596 | }
597 |
598 | @NotNull
599 | @Override
600 | public String getDisplayName() {
601 | return DISPLAY_NAME;
602 | }
603 | }
604 |
--------------------------------------------------------------------------------