Compare commits

...

144 Commits

Author SHA1 Message Date
Sheldan
0390d7c8ca [maven-release-plugin] prepare for next development iteration 2023-11-23 01:04:11 +01:00
Sheldan
61412f434c [maven-release-plugin] prepare release sissi-1.4.23 2023-11-23 01:04:07 +01:00
Sheldan
12e69a18fb [SIS-xxx] preparing for release 2023-11-23 01:02:35 +01:00
Sheldan
170ddd9c33 [SIS-xxx] fixing cache setup to add additional caches instead of overwriting the provided configuration 2023-11-23 01:01:40 +01:00
Sheldan
de8bbdcbee [SIS-xxx] fixing rest api image version 2023-11-21 00:39:07 +01:00
Sheldan
b5bf53fb6a [maven-release-plugin] prepare for next development iteration 2023-11-21 00:25:40 +01:00
Sheldan
159326dde3 [maven-release-plugin] prepare release sissi-1.4.22 2023-11-21 00:25:32 +01:00
Sheldan
7124d4e1d8 [SIS-xxx] prepare for release 2023-11-21 00:24:47 +01:00
Sheldan
c6f20d617d [SIS-xxx] adding rendering of current Debra donation information
updating for Debra 2023 campaign
adding internal rest api for debra information
adding a debra button to receive information
2023-11-21 00:23:03 +01:00
Sheldan
7449c05462 [maven-release-plugin] prepare for next development iteration 2023-11-14 23:41:52 +01:00
Sheldan
6bd0f0eaa2 [maven-release-plugin] prepare release sissi-1.4.21 2023-11-14 23:41:49 +01:00
Sheldan
9b8a3bcc1e [SIS-xxx] preparing for release 2023-11-14 23:40:38 +01:00
Sheldan
7b11a88256 [SIS-xxx] enabling external configuration of db pool size 2023-11-14 23:39:52 +01:00
Sheldan
30289e1166 [SIS-xxx] fixing template content 2023-11-01 09:20:14 +01:00
Sheldan
0bab3bbe69 [SIS-xxx] fixing template keys 2023-10-29 21:26:50 +01:00
Sheldan
6f75c76fc4 [maven-release-plugin] prepare for next development iteration 2023-10-29 19:16:26 +01:00
Sheldan
0d9aa4ae64 [maven-release-plugin] prepare release sissi-1.4.20 2023-10-29 19:16:22 +01:00
Sheldan
c6b0bf582b [SIS-xxx] bump version 2023-10-29 19:15:58 +01:00
Sheldan
dcd94ba5c0 [maven-release-plugin] prepare for next development iteration 2023-10-29 19:01:54 +01:00
Sheldan
98ae8ada8b [maven-release-plugin] prepare release sissi-1.4.19 2023-10-29 19:01:48 +01:00
Sheldan
96d3918a4d [SIS-25] adding rss news module
updating abstracto version
2023-10-29 19:00:16 +01:00
Sheldan
0d6f71baac [maven-release-plugin] prepare for next development iteration 2023-09-26 23:16:39 +02:00
Sheldan
d3421a6f95 [maven-release-plugin] prepare release sissi-1.4.18 2023-09-26 23:16:35 +02:00
Sheldan
0722bd6320 [SIS-xxx] preparing for release 2023-09-26 23:12:54 +02:00
Sheldan
a910870259 [SIS-xxx] removing discriminator and adapting join/leave text 2023-09-26 23:12:34 +02:00
Sheldan
18cc97600f [maven-release-plugin] prepare for next development iteration 2023-09-07 23:52:52 +02:00
Sheldan
a1ea57a0f3 [maven-release-plugin] prepare release sissi-1.4.17 2023-09-07 23:52:48 +02:00
Sheldan
23b554c93f [SIS-xxx] preparing for new release and upgrade of abstracto 2023-09-07 23:49:24 +02:00
Sheldan
5671c5019a [maven-release-plugin] prepare for next development iteration 2023-09-07 21:35:48 +02:00
Sheldan
fbb0876c7f [maven-release-plugin] prepare release sissi-1.4.16 2023-09-07 21:35:44 +02:00
Sheldan
bbaee9a47a [SIS-xxx] preparing for new release and upgrade of abstracto 2023-09-07 21:16:17 +02:00
Sheldan
b9f2a06173 [maven-release-plugin] prepare for next development iteration 2023-09-03 14:05:45 +02:00
Sheldan
55ba69bb0f [maven-release-plugin] prepare release sissi-1.4.15 2023-09-03 14:05:42 +02:00
Sheldan
9ed71b8660 [SIS-xxx] preparing for new release and upgrade of abstracto 2023-09-03 14:04:54 +02:00
Sheldan
28cf141a02 [maven-release-plugin] prepare for next development iteration 2023-09-03 01:18:54 +02:00
Sheldan
03ca4d128d [maven-release-plugin] prepare release sissi-1.4.14 2023-09-03 01:18:47 +02:00
Sheldan
ff2029819d [SIS-xxx] preparing for new release 2023-09-03 01:15:18 +02:00
Sheldan
a26503650e [SIS-24] adding ability to only search quotes of a given parameter for a text
changing quote search to return a random matching quote
adding local function to tilt to update packages
2023-09-03 00:48:15 +02:00
Sheldan
7a37f7e040 [SIS-xxx] fixing tilt file caching
updating dashboards to filter for namespace
upgrading to new abstracto version -snapshot
2023-09-02 21:28:13 +02:00
Sheldan
0073d5b069 [maven-release-plugin] prepare for next development iteration 2023-08-30 01:23:11 +02:00
Sheldan
c210e34437 [maven-release-plugin] prepare release sissi-1.4.13 2023-08-30 01:23:06 +02:00
Sheldan
7a43b23d68 [SIS-xxx] fixing single core jdk 17 issue
preparing for new version
2023-08-30 01:22:28 +02:00
Sheldan
7c15648e75 [maven-release-plugin] prepare for next development iteration 2023-08-30 01:08:06 +02:00
Sheldan
d629bf633f [maven-release-plugin] prepare release sissi-1.4.12 2023-08-30 01:08:02 +02:00
Sheldan
4115037141 [SIS-xxx] prepare for release 2023-08-30 01:07:38 +02:00
Sheldan
5d02bb99d4 [SIS-xxx] corrected hook weight for db credentials 2023-08-30 01:07:08 +02:00
Sheldan
96bdc3d089 [maven-release-plugin] prepare for next development iteration 2023-08-30 00:50:04 +02:00
Sheldan
71546fcc89 [maven-release-plugin] prepare release sissi-1.4.11 2023-08-30 00:50:00 +02:00
Sheldan
22c330c7e4 [SIS-xxx] reducing hook weight for db credentials 2023-08-30 00:49:24 +02:00
Sheldan
fd402a966a [SIS-xxx] changing db secrets to be created before installation 2023-08-30 00:49:03 +02:00
Sheldan
b3029deea4 [maven-release-plugin] prepare for next development iteration 2023-08-30 00:34:50 +02:00
Sheldan
24b23c445f [maven-release-plugin] prepare release sissi-1.4.10 2023-08-30 00:34:46 +02:00
Sheldan
a9c44e24f1 prepare for release 2023-08-30 00:34:22 +02:00
Sheldan
7deee4008d correct config deployment job helm hooks 2023-08-30 00:33:37 +02:00
Sheldan
2ab922cc84 [maven-release-plugin] prepare for next development iteration 2023-08-30 00:12:31 +02:00
Sheldan
c6a7b60e41 [maven-release-plugin] prepare release sissi-1.4.9 2023-08-30 00:12:27 +02:00
Sheldan
49974648b6 making probes configurable 2023-08-30 00:11:48 +02:00
Sheldan
cd7580f45d [maven-release-plugin] prepare for next development iteration 2023-08-29 23:55:21 +02:00
Sheldan
076afb35b4 [maven-release-plugin] prepare release sissi-1.4.8 2023-08-29 23:55:17 +02:00
Sheldan
f901aeaefc [SIS-xxx] renaming sissi run image 2023-08-29 23:54:47 +02:00
Sheldan
6b37de3db5 [maven-release-plugin] prepare for next development iteration 2023-08-29 23:26:28 +02:00
Sheldan
0b80e351a5 [maven-release-plugin] prepare release sissi-1.4.7 2023-08-29 23:26:24 +02:00
Sheldan
3fe47ab682 [SIS-xxx] prepare for release 2023-08-29 23:25:56 +02:00
Sheldan
9b69fe5f2d [SIS-xxx] removing grafana credentials secret from helm chart 2023-08-29 23:25:03 +02:00
Sheldan
953eb1b4ff [maven-release-plugin] prepare for next development iteration 2023-08-28 00:06:52 +02:00
Sheldan
876dd82d87 [maven-release-plugin] prepare release sissi-1.4.6 2023-08-28 00:06:34 +02:00
Sheldan
454d66c71e [SIS-xxx] changing repository for bot deployment in k8s 2023-08-28 00:05:59 +02:00
Sheldan
87f72a077d [maven-release-plugin] prepare for next development iteration 2023-08-27 23:56:33 +02:00
Sheldan
62c27d1461 [maven-release-plugin] prepare release sissi-1.4.5 2023-08-27 23:56:29 +02:00
Sheldan
3b2bbb1dce [SIS-xxx] preparing for 1.4.5 release 2023-08-27 23:55:36 +02:00
Sheldan
8f40a95cbe [maven-release-plugin] prepare for next development iteration 2023-08-27 23:48:06 +02:00
Sheldan
5e9518de71 [maven-release-plugin] prepare release sissi-1.4.4 2023-08-27 23:48:02 +02:00
Sheldan
0ecab3b21b [SIS-xxx] preparing for 1.4.4 release 2023-08-27 23:42:57 +02:00
Sheldan
5e0e546eb4 [maven-release-plugin] prepare for next development iteration 2023-08-27 23:32:57 +02:00
Sheldan
ade0d4e57d [maven-release-plugin] prepare release sissi-1.4.3 2023-08-27 23:32:53 +02:00
Sheldan
26b08c593c [SIS-xxx] preparing for 1.4.2 release 2023-08-27 23:32:25 +02:00
Sheldan
888f2bd402 [SIS-xxx] fixing env file loading 2023-08-27 23:31:12 +02:00
Sheldan
eb369d9116 [maven-release-plugin] prepare for next development iteration 2023-08-27 23:17:25 +02:00
Sheldan
f2acdbdd81 [maven-release-plugin] prepare release sissi-1.4.2 2023-08-27 23:17:21 +02:00
Sheldan
d65f6d380f [SIS-xxx] introducing helm chart
restructuring
2023-08-27 23:12:01 +02:00
Sheldan
30d45952be [maven-release-plugin] prepare for next development iteration 2023-07-22 18:29:23 +02:00
Sheldan
068cbdb33c [maven-release-plugin] prepare release sissi-1.4.1 2023-07-22 18:29:19 +02:00
Sheldan
effca93b7a [SIS-xxx] upgrading to abstracto 1.5.1 2023-07-22 18:26:51 +02:00
Sheldan
ac815b5972 [SIS-22] fixing always url encoding the location for meetups 2023-07-10 01:31:21 +02:00
Sheldan
e6cccef48d [SIS-21] fixing meetup title not being escaped in meetup list response 2023-07-10 01:07:21 +02:00
Sheldan
4f35aa56f4 [maven-release-plugin] prepare for next development iteration 2023-07-09 23:44:53 +02:00
Sheldan
66204ca061 [maven-release-plugin] prepare release sissi-1.4.0 2023-07-09 23:44:49 +02:00
Sheldan
2960173517 [SIS-xxx] prepare for release
upgrading to java 17
upgrading to abstracto 1.5.0
2023-07-09 23:37:49 +02:00
Sheldan
b810c25613 [maven-release-plugin] prepare for next development iteration 2023-06-04 21:41:53 +02:00
Sheldan
583c2cc4be [maven-release-plugin] prepare release sissi-1.3.29 2023-06-04 21:41:49 +02:00
Sheldan
0c36564331 [SIS-xxx] prepare for release 2023-06-04 21:14:24 +02:00
Sheldan
d7bd458bed [SIS-xxx] limiting shown meetups to the ones created in the channel 2023-05-23 01:11:45 +02:00
Sheldan
bbcd77e3c8 [maven-release-plugin] prepare for next development iteration 2023-05-19 00:47:13 +02:00
Sheldan
ec166167f4 [maven-release-plugin] prepare release sissi-1.3.28 2023-05-19 00:47:09 +02:00
Sheldan
d5b4835d1f [SIS-xxx] preparing for release 2023-05-19 00:43:40 +02:00
Sheldan
41304551e4 [SIS-xxx] preparing for release 2023-05-19 00:25:39 +02:00
Sheldan
279959f256 [maven-release-plugin] prepare for next development iteration 2023-05-19 00:06:57 +02:00
Sheldan
7cc0e8e21c [maven-release-plugin] prepare release sissi-1.3.27 2023-05-19 00:06:53 +02:00
Sheldan
87e9122c41 [SIS-xxx] preparing for release 2023-05-19 00:06:18 +02:00
Sheldan
f22cc03e14 [maven-release-plugin] prepare for next development iteration 2023-05-18 23:22:59 +02:00
Sheldan
84b103cfba [maven-release-plugin] prepare release sissi-1.3.26 2023-05-18 23:22:54 +02:00
Sheldan
a015e01c40 [SIS-xxx] preparing for release 2023-05-18 23:22:21 +02:00
Sheldan
981db7b43f [SIS-20] adding feature to attach an ics file to meetups
can be toggled via feature mode
2023-05-18 22:49:50 +02:00
Sheldan
65560991bc [maven-release-plugin] prepare for next development iteration 2023-03-29 23:24:32 +02:00
Sheldan
b914ba035f [maven-release-plugin] prepare release sissi-1.3.25 2023-03-29 23:24:27 +02:00
Sheldan
d3e77f17ec [SIS-xxx] preparing for release 2023-03-29 23:23:09 +02:00
Sheldan
9498458165 [SIS-xxx] enabling emote usage tracking 2023-03-29 23:22:45 +02:00
Sheldan
03a112fb43 [SIS-xxx] update license 2023-03-20 23:18:11 +01:00
Sheldan
782773fef5 [SIS-xxx] fixed missing parameter for bot service in docker compose 2023-03-19 10:33:43 +01:00
Sheldan
5076e79c1d [maven-release-plugin] prepare for next development iteration 2023-03-19 00:39:42 +01:00
Sheldan
ac1baa4734 [maven-release-plugin] prepare release sissi-1.3.24 2023-03-19 00:39:39 +01:00
Sheldan
2c508665be [SIS-xxx] preparing for release 2023-03-19 00:13:48 +01:00
Sheldan
1023951a76 [maven-release-plugin] prepare for next development iteration 2023-02-26 11:23:30 +01:00
Sheldan
a1923427a0 [maven-release-plugin] prepare release sissi-1.3.23 2023-02-26 11:23:26 +01:00
Sheldan
4c8ca91712 [SIS-xxx] preparing for release 2023-02-26 11:03:00 +01:00
Sheldan
f78ab3372e [SIS-17] fixing meetup failing to create if no location is provided 2023-02-22 16:16:08 +01:00
Sheldan
c70815f25d [SIS-xxx] changing lvl up notification message 2023-02-20 11:44:04 +01:00
Sheldan
7ee3653ab9 [maven-release-plugin] prepare for next development iteration 2023-02-15 00:39:16 +01:00
Sheldan
bb60d767a7 [maven-release-plugin] prepare release sissi-1.3.22 2023-02-15 00:39:12 +01:00
Sheldan
5a34447abe [SIS-xxx] preparing for release 2023-02-15 00:37:00 +01:00
Sheldan
bbcfcfcc6f [maven-release-plugin] prepare for next development iteration 2023-02-04 18:48:28 +01:00
Sheldan
75187cae00 [maven-release-plugin] prepare release sissi-1.3.21 2023-02-04 18:48:24 +01:00
Sheldan
710a039707 [SIS-xxx] preparing for release 2023-02-04 18:36:32 +01:00
Sheldan
b7dcb72e7a [maven-release-plugin] prepare for next development iteration 2023-02-04 17:44:50 +01:00
Sheldan
ead142cf5d [maven-release-plugin] prepare release sissi-1.3.20 2023-02-04 17:44:46 +01:00
Sheldan
11bf8cc9bc [SIS-xxx] preparing for release 2023-02-04 17:42:40 +01:00
Sheldan
1083f93d2c [maven-release-plugin] prepare for next development iteration 2023-02-04 15:59:14 +01:00
Sheldan
6de38d3bea [maven-release-plugin] prepare release sissi-1.3.19 2023-02-04 15:59:10 +01:00
Sheldan
9de6eb4b8b [SIS-xxx] preparing for release 2023-02-04 15:57:17 +01:00
Sheldan
df1392bf84 [SIS-xxx] preparing for release 2023-02-04 15:53:39 +01:00
Sheldan
6e065de915 [maven-release-plugin] prepare for next development iteration 2023-01-12 08:53:33 +01:00
Sheldan
a1820aea67 [maven-release-plugin] prepare release sissi-1.3.18 2023-01-12 08:53:29 +01:00
Sheldan
09c113c6bc [SIS-16] additional features for meetups to:
define a location
update properties
being able to define which type of decisions should receive meetup notifications
reference the meetup in a notification

preparing for release
2023-01-12 01:43:24 +01:00
Sheldan
d080292e85 [maven-release-plugin] prepare for next development iteration 2022-12-21 14:01:37 +01:00
Sheldan
0e9ea8cf1a [maven-release-plugin] prepare release sissi-1.3.17 2022-12-21 14:01:33 +01:00
Sheldan
5d1037f66d [SIS-xxx] preparing for release 2022-12-21 13:57:25 +01:00
Sheldan
eb6251aae0 [maven-release-plugin] prepare for next development iteration 2022-12-13 00:35:06 +01:00
Sheldan
caccf8b405 [maven-release-plugin] prepare release sissi-1.3.16 2022-12-13 00:35:01 +01:00
Sheldan
5390c0e53e [SIS-15] adding optional donation listing to donation command
refactoring to use the proper API instead of relying on parsing
prepare for release
2022-12-13 00:31:14 +01:00
Sheldan
8732064764 [maven-release-plugin] prepare for next development iteration 2022-12-10 20:46:18 +01:00
Sheldan
46833e024f [maven-release-plugin] prepare release sissi-1.3.15 2022-12-10 20:46:14 +01:00
Sheldan
e459ef77f3 [SIS-xxx] prepare for release 2022-12-10 20:45:52 +01:00
Sheldan
a827d7e946 [SIS-xxx] dont close connection 2022-12-10 20:45:33 +01:00
Sheldan
85da8684a4 [maven-release-plugin] prepare for next development iteration 2022-12-10 19:01:40 +01:00
372 changed files with 8726 additions and 3355 deletions

View File

@@ -17,11 +17,14 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Set up JDK 1.8
uses: actions/setup-java@v1
- uses: actions/checkout@v3
with:
java-version: 1.8
persist-credentials: false
- name: Set up JDK 17
uses: actions/setup-java@v3
with:
distribution: 'corretto'
java-version: 17
- name: Build with Maven
run: mvn -s settings.xml -B install --file pom.xml
env:

View File

@@ -6,13 +6,14 @@ jobs:
publish:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
with:
persist-credentials: false
- name: Set up Java for publishing to GitHub Packages
uses: actions/setup-java@v1
uses: actions/setup-java@v3
with:
java-version: 1.8
distribution: 'corretto'
java-version: 17
- name: Load current version
id: version
run: echo "version=$(mvn -s settings.xml --file pom.xml -q -Dexec.executable="echo" -Dexec.args='${project.version}' --non-recursive exec:exec)" >> $GITHUB_ENV
@@ -22,17 +23,26 @@ jobs:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
user: Sheldan
token: ${{ secrets.ABSTRACTO_PAT }}
- name: Login to GitHub Packages Docker Registry
uses: docker/login-action@v1
- name: Login to Harbor
uses: docker/login-action@v2
with:
registry: docker.pkg.github.com
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Push deployment container
registry: harbor.sheldan.dev
username: ${{ secrets.HARBOR_USERNAME }}
password: ${{ secrets.HARBOR_TOKEN }}
- name: Load env files
id: dotenv
uses: falti/dotenv-action@v1.0.4
with:
path: ./deployment/image-packaging/src/main/docker/.env
- name: Push container
working-directory: ./deployment/image-packaging/src/main/docker
run: docker-compose build && docker-compose push
env:
REGISTRY_PREFIX: docker.pkg.github.com/sheldan/sissi/
VERSION: ${{ env.version }}
ABSTRACTO_VERSION: 1.4.14
ABSTRACTO_REGISTRY_PREFIX: docker.pkg.github.com/sheldan/abstracto/
REGISTRY_PREFIX: ${{ steps.dotenv.outputs.registry_prefix }}
VERSION: ${{ steps.dotenv.outputs.version }}
- name: Helm push
working-directory: ./deployment/helm/
run: |-
helm registry login -u '${{ secrets.HARBOR_USERNAME }}' -p '${{ secrets.HARBOR_TOKEN }}' harbor.sheldan.dev
helm package sissi
helm push sissi*.tgz oci://harbor.sheldan.dev/sissi

View File

@@ -1,6 +1,6 @@
MIT License
Copyright (c) 2022 Sheldan
Copyright (c) 2023 Sheldan
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

41
Tiltfile Normal file
View File

@@ -0,0 +1,41 @@
allow_k8s_contexts('k8s-cluster')
load('ext://restart_process', 'docker_build_with_restart')
registry = 'harbor.sheldan.dev/sissi/'
local_resource(
'sissi-java-compile',
' mvn install && ' +
' rm -rf application/executable/target/jar-staging && ' +
' unzip -o application/executable/target/sissi-exec.jar -d application/executable/target/jar-staging && ' +
' rsync --delete --delete-excluded --inplace --checksum --exclude="*-SNAPSHOT.jar" -r application/executable/target/jar-staging/ application/executable/target/jar && ' +
' rm -rf application/executable/target/jar/snapshots && ' +
' mkdir application/executable/target/jar/snapshots && ' +
' rsync --delete --delete-excluded --inplace --checksum --include="*/" --include="*-SNAPSHOT.jar" --exclude="*" -r application/executable/target/jar-staging/BOOT-INF/lib/ application/executable/target/jar/snapshots',
deps=['pom.xml'], auto_init=False, trigger_mode = TRIGGER_MODE_MANUAL)
docker_build_with_restart(
registry + 'sissi-bot',
'./application/executable/target/jar',
entrypoint=['java', '-noverify', '-agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=*:5005', '-cp', '.:./lib/*', 'dev.sheldan.sissi.executable.Application'],
dockerfile='./application/executable/Dockerfile',
live_update=[
sync('./application/executable/target/jar/BOOT-INF/lib', '/app/lib'),
sync('./application/executable/target/jar/META-INF', '/app/META-INF'),
sync('./application/executable/target/jar/BOOT-INF/classes', '/app'),
sync('./application/executable/target/jar/snapshots', '/app/lib')
],
)
docker_build(registry + 'sissi-db-data', 'deployment/image-packaging/src/main/docker/db-data/')
docker_build(registry + 'sissi-rest-api', 'deployment/image-packaging/src/main/docker/rest-api/')
docker_build(registry + 'sissi-template-data', 'deployment/image-packaging/src/main/docker/template-data/')
k8s_yaml(helm('deployment/helm/sissi', values=
['./../Sissi-environments/argocd/apps/sissi/values/local/values.yaml',
'secrets://./../Sissi-environments/argocd/apps/sissi/values/local/values.secrets.yaml']
))
local_resource('fetch-packages', 'mvn install -f deployment/image-packaging/pom.xml', auto_init=False, trigger_mode = TRIGGER_MODE_MANUAL)
k8s_resource('chart-sissi', port_forwards='5005:5005')

View File

@@ -0,0 +1,10 @@
FROM amazoncorretto:17.0.8-alpine3.18
RUN apk add entr
WORKDIR /app
ADD BOOT-INF/lib/ /app/lib
ADD snapshots/ /app/lib
ADD META-INF /app/META-INF
ADD BOOT-INF/classes /app
ENTRYPOINT java -cp .:./lib/* dev.sheldan.sissi.executable.Application

View File

@@ -3,17 +3,13 @@
<parent>
<groupId>dev.sheldan.sissi.application</groupId>
<artifactId>application</artifactId>
<version>1.3.14</version>
<version>1.4.24-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>executable</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties>
<build>
<finalName>sissi</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
@@ -75,6 +71,11 @@
<artifactId>webservices-impl</artifactId>
</dependency>
<dependency>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>twitch-impl</artifactId>
</dependency>
<dependency>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>utility-impl</artifactId>
@@ -130,6 +131,11 @@
<artifactId>logging-impl</artifactId>
</dependency>
<dependency>
<groupId>dev.sheldan.abstracto.modules</groupId>
<artifactId>statistic-impl</artifactId>
</dependency>
<!-- sissi modules -->
<dependency>
@@ -144,6 +150,12 @@
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>dev.sheldan.sissi.application.module</groupId>
<artifactId>rss-news</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>dev.sheldan.sissi.application.module</groupId>
<artifactId>debra</artifactId>

View File

@@ -0,0 +1,18 @@
package dev.sheldan.sissi.executable;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.web.SecurityFilterChain;
@Configuration
public class SecurityConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.authorizeHttpRequests((authorize) -> authorize
.anyRequest().permitAll());
return http.build();
}
}

View File

@@ -1,21 +0,0 @@
spring.datasource.url=jdbc:postgresql://localhost:5432/abstracto
spring.datasource.username=abstracto
spring.datasource.password=abstracto
spring.jpa.properties.hibernate.default_schema=abstracto
spring.quartz.jdbc.initialize-schema=never
spring.jpa.hibernate.ddl-auto = none
spring.jpa.show-sql = false
spring.jpa.properties.hibernate.format_sql = true
log4j.logger.org.hibernate.SQL=trace
log4j.logger.org.hibernate.type.descriptor.sql=trace
log4j.logger.org.hibernate.type=trace
management.metrics.tags.application=sissi
spring.security.user.name=abstracto
spring.security.user.password=password
spring.security.user.roles=USER
spring.application.name=Sissi

View File

@@ -1,11 +1,14 @@
spring.datasource.url=jdbc:postgresql://${DB_HOST}:${DB_PORT}/${DB_NAME}
spring.datasource.username= ${DB_USER}
spring.datasource.password= ${DB_PASS}
spring.jpa.hibernate.default_schema=${DB_NAME}
spring.datasource.hikari.maximum-pool-size=${hikariPoolSize}
spring.jpa.hibernate.default_schema=abstracto
spring.jpa.properties.hibernate.default_schema=abstracto
spring.quartz.jdbc.initialize-schema=never
management.metrics.tags.application=Sissi
spring.security.user.name= ${REST_USER_NAME}
spring.security.user.password= ${REST_PASSWORD}
spring.security.user.roles=USER
management.endpoint.health.probes.enabled=true
management.health.livenessState.enabled=true
management.health.readinessState.enabled=true
spring.application.name=Sissi
spring.application.name=Sissi
spring.main.allow-circular-references=true

View File

@@ -3,7 +3,7 @@
<parent>
<groupId>dev.sheldan.sissi</groupId>
<artifactId>sissi</artifactId>
<version>1.3.14</version>
<version>1.4.24-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -22,6 +22,18 @@
<scope>import</scope>
<type>pom</type>
</dependency>
<dependency>
<groupId>org.apache.jena</groupId>
<artifactId>apache-jena-libs</artifactId>
<version>${apache-jena.version}</version>
<scope>import</scope>
<type>pom</type>
</dependency>
<dependency>
<groupId>com.apptasticsoftware</groupId>
<artifactId>rssreader</artifactId>
<version>${rssreader.version}</version>
</dependency>
</dependencies>
</dependencyManagement>

View File

@@ -3,7 +3,7 @@
<parent>
<groupId>dev.sheldan.sissi.application.module.custom</groupId>
<artifactId>sissi-customizations</artifactId>
<version>1.3.14</version>
<version>1.4.24-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -3,7 +3,7 @@
<parent>
<artifactId>application</artifactId>
<groupId>dev.sheldan.sissi.application</groupId>
<version>1.3.14</version>
<version>1.4.24-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -3,13 +3,33 @@
<parent>
<groupId>dev.sheldan.sissi.application</groupId>
<artifactId>sissi-modules</artifactId>
<version>1.3.14</version>
<version>1.4.24-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>dev.sheldan.sissi.application.module</groupId>
<artifactId>debra</artifactId>
<dependencies>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.ehcache</groupId>
<artifactId>ehcache</artifactId>
<classifier>jakarta</classifier>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>

View File

@@ -0,0 +1,69 @@
package dev.sheldan.sissi.module.debra.api;
import dev.sheldan.sissi.module.debra.model.api.*;
import dev.sheldan.sissi.module.debra.service.DonationService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import static dev.sheldan.sissi.module.debra.config.DebraFeatureConfig.DEBRA_DONATION_NOTIFICATION_SERVER_ID_ENV_NAME;
@RestController
@RequestMapping(value = "/debra")
public class DebraDonationStatusController {
@Autowired
private DonationService donationService;
@GetMapping(value = "/latestDonations", produces = "application/json")
public DonationStats getLatestDonations() {
Long serverId = Long.parseLong(System.getenv(DEBRA_DONATION_NOTIFICATION_SERVER_ID_ENV_NAME));
DonationsResponse donationResponse = donationService.getCachedDonationAmount(serverId);
List<DonationInfo> donations = donationService.getLatestDonations(donationResponse, Integer.MAX_VALUE)
.stream()
.map(DonationInfo::fromDonationItemModel)
.toList();
return DonationStats
.builder()
.totalAmount(donationResponse.getPage().getCollected())
.donations(donations)
.build();
}
@GetMapping(value = "/highestDonations", produces = "application/json")
public DonationStats getHighestDonations() {
Long serverId = Long.parseLong(System.getenv(DEBRA_DONATION_NOTIFICATION_SERVER_ID_ENV_NAME));
DonationsResponse donationResponse = donationService.getCachedDonationAmount(serverId);
List<DonationInfo> donations = donationService.getHighestDonations(donationResponse, Integer.MAX_VALUE)
.stream()
.map(DonationInfo::fromDonationItemModel)
.toList();
return DonationStats
.builder()
.totalAmount(donationResponse.getPage().getCollected())
.donations(donations)
.build();
}
@GetMapping(value = "/campaignInfo", produces = "application/json")
public CampaignInfo getCampaignInfo() {
Long serverId = Long.parseLong(System.getenv(DEBRA_DONATION_NOTIFICATION_SERVER_ID_ENV_NAME));
DonationsResponse donationResponse = donationService.getCachedDonationAmount(serverId);
Description pageObject = donationResponse.getPage();
return CampaignInfo
.builder()
.collected(pageObject.getCollected())
.collectedNet(pageObject.getCollectedNet())
.donationCount(donationResponse.getDonationCount())
.currency(pageObject.getCurrency())
.percent(pageObject.getPercent())
.displayName(pageObject.getDisplayName())
.slug(pageObject.getSlug())
.target(pageObject.getTarget())
.build();
}
}

View File

@@ -0,0 +1,91 @@
package dev.sheldan.sissi.module.debra.commands;
import dev.sheldan.abstracto.core.command.UtilityModuleDefinition;
import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand;
import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
import dev.sheldan.abstracto.core.command.config.HelpInfo;
import dev.sheldan.abstracto.core.command.config.Parameter;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.interaction.InteractionService;
import dev.sheldan.abstracto.core.interaction.slash.SlashCommandConfig;
import dev.sheldan.abstracto.core.interaction.slash.parameter.SlashCommandParameterService;
import dev.sheldan.sissi.module.debra.config.DebraFeatureDefinition;
import dev.sheldan.sissi.module.debra.config.DebraSlashCommandNames;
import dev.sheldan.sissi.module.debra.service.DonationService;
import net.dv8tion.jda.api.entities.channel.middleman.GuildMessageChannel;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@Component
public class DebraInfoButton extends AbstractConditionableCommand {
private static final String DEBRA_INFO_BUTTON = "debraInfoButton";
private static final String DEBRA_INFO_BUTTON_RESPONSE_TEMPLATE_KEY = "debraInfoButton_response";
private static final String TARGET_CHANNEL_PARAMETER_KEY = "targetChannel";
@Autowired
private SlashCommandParameterService slashCommandParameterService;
@Autowired
private InteractionService interactionService;
@Autowired
private DonationService donationService;
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
GuildMessageChannel targetChannel = slashCommandParameterService.getCommandOption(TARGET_CHANNEL_PARAMETER_KEY, event, GuildMessageChannel.class);
return donationService.sendDebraInfoButtonMessage(targetChannel)
.thenCompose(unused -> interactionService.replyEmbed(DEBRA_INFO_BUTTON_RESPONSE_TEMPLATE_KEY, event))
.thenApply(interactionHook -> CommandResult.fromSuccess());
}
@Override
public CommandConfiguration getConfiguration() {
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.build();
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(DebraSlashCommandNames.DEBRA_INTERNAL)
.commandName("debrainfobutton")
.build();
Parameter targetChannelParameter = Parameter
.builder()
.templated(true)
.name(TARGET_CHANNEL_PARAMETER_KEY)
.type(GuildMessageChannel.class)
.build();
List<Parameter> parameters = Arrays.asList(targetChannelParameter);
return CommandConfiguration.builder()
.name(DEBRA_INFO_BUTTON)
.module(UtilityModuleDefinition.UTILITY)
.templated(true)
.slashCommandConfig(slashCommandConfig)
.async(true)
.slashCommandOnly(true)
.parameters(parameters)
.supportsEmbedException(true)
.causesReaction(false)
.help(helpInfo)
.build();
}
@Override
public FeatureDefinition getFeature() {
return DebraFeatureDefinition.DEBRA;
}
}

View File

@@ -4,24 +4,32 @@ import dev.sheldan.abstracto.core.command.UtilityModuleDefinition;
import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand;
import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
import dev.sheldan.abstracto.core.command.config.HelpInfo;
import dev.sheldan.abstracto.core.command.config.Parameter;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException;
import dev.sheldan.abstracto.core.interaction.InteractionService;
import dev.sheldan.abstracto.core.interaction.slash.SlashCommandConfig;
import dev.sheldan.abstracto.core.interaction.slash.parameter.SlashCommandParameterService;
import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.templating.model.MessageToSend;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import dev.sheldan.abstracto.core.utils.FutureUtils;
import dev.sheldan.sissi.module.debra.config.DebraFeatureDefinition;
import dev.sheldan.sissi.module.debra.config.DebraSlashCommandNames;
import dev.sheldan.sissi.module.debra.converter.DonationConverter;
import dev.sheldan.sissi.module.debra.model.api.DonationsResponse;
import dev.sheldan.sissi.module.debra.model.commands.DonationsModel;
import dev.sheldan.sissi.module.debra.service.DonationService;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.math.BigDecimal;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@@ -30,6 +38,8 @@ public class Donations extends AbstractConditionableCommand {
private static final String DONATIONS_COMMAND_NAME = "donations";
private static final String DONATIONS_RESPONSE_TEMPLATE_KEY = "donations_response";
private static final String SELECTION_PARAMETER = "type";
private static final String SELECTION_VALUE_PARAMETER = "parametervalue";
@Autowired
private ChannelService channelService;
@@ -43,26 +53,77 @@ public class Donations extends AbstractConditionableCommand {
@Autowired
private TemplateService templateService;
@Autowired
private DonationConverter donationConverter;
@Autowired
private SlashCommandParameterService slashCommandParameterService;
@Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
MessageToSend messageToSend = getDonationMessageToSend();
List<Object> parameters = commandContext.getParameters().getParameters();
MessageToSend messageToSend;
if(parameters.isEmpty()) {
messageToSend = getDonationMessageToSend(commandContext.getGuild().getIdLong(), null, null);
} else {
String type = (String) parameters.get(0);
Integer selectionValue = (Integer) parameters.get(1);
Integer top = null;
Integer latest = null;
switch (type) {
case "top": top = selectionValue; break;
default:
case "latest" :
latest = selectionValue; break;
}
messageToSend = getDonationMessageToSend(commandContext.getGuild().getIdLong(), top, latest);
}
return FutureUtils.toSingleFutureGeneric(channelService.sendMessageToSendToChannel(messageToSend, commandContext.getChannel()))
.thenApply(unused -> CommandResult.fromSuccess());
}
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
MessageToSend messageToSend = getDonationMessageToSend();
String selectionType = null;
if(slashCommandParameterService.hasCommandOption(SELECTION_PARAMETER, event)) {
selectionType = slashCommandParameterService.getCommandOption(SELECTION_PARAMETER, event, String.class);
}
Integer selectionValue = 5;
if(slashCommandParameterService.hasCommandOption(SELECTION_VALUE_PARAMETER, event)) {
selectionValue = slashCommandParameterService.getCommandOption(SELECTION_VALUE_PARAMETER, event, Integer.class);
}
if(selectionValue > 20) {
selectionValue = 5;
}
Integer top = null;
Integer latest = null;
if(selectionType != null) {
switch (selectionType) {
case "top": top = selectionValue; break;
default:
case "latest" :
latest = selectionValue; break;
}
}
MessageToSend messageToSend = getDonationMessageToSend(event.getGuild().getIdLong(), top, latest);
return interactionService.replyMessageToSend(messageToSend, event)
.thenApply(interactionHook -> CommandResult.fromSuccess());
}
private MessageToSend getDonationMessageToSend() {
BigDecimal currentDonationAmount = donationService.fetchCurrentDonationAmount();
DonationsModel donationModel = DonationsModel
.builder()
.donationAmount(currentDonationAmount)
.build();
private MessageToSend getDonationMessageToSend(Long serverId, Integer top, Integer latest) {
DonationsModel donationModel;
DonationsResponse donationResponse = donationService.fetchCurrentDonationAmount(serverId);
donationModel = donationConverter.convertDonationResponse(donationResponse);
if(top != null) {
donationModel.setDonations(donationService.getHighestDonations(donationResponse, top));
donationModel.setType(DonationsModel.DonationType.TOP);
} else if(latest != null) {
donationModel.setType(DonationsModel.DonationType.LATEST);
donationModel.setDonations(donationService.getLatestDonations(donationResponse, latest));
} else {
donationModel.setDonations(new ArrayList<>());
}
return templateService.renderEmbedTemplate(DONATIONS_RESPONSE_TEMPLATE_KEY, donationModel);
}
@@ -77,15 +138,35 @@ public class Donations extends AbstractConditionableCommand {
.builder()
.enabled(true)
.rootCommandName(DebraSlashCommandNames.DEBRA)
.commandName("donations")
.commandName(DONATIONS_COMMAND_NAME)
.build();
Parameter selectionParameter = Parameter
.builder()
.templated(true)
.name(SELECTION_PARAMETER)
.optional(true)
.type(String.class)
.build();
Parameter selectionValueParameter = Parameter
.builder()
.templated(true)
.name(SELECTION_VALUE_PARAMETER)
.optional(true)
.type(Integer.class)
.build();
List<Parameter> parameters = Arrays.asList(selectionParameter, selectionValueParameter);
return CommandConfiguration.builder()
.name(DONATIONS_COMMAND_NAME)
.module(UtilityModuleDefinition.UTILITY)
.templated(true)
.slashCommandConfig(slashCommandConfig)
.async(true)
.parameters(parameters)
.supportsEmbedException(true)
.causesReaction(false)
.help(helpInfo)

View File

@@ -0,0 +1,33 @@
package dev.sheldan.sissi.module.debra.config;
import lombok.extern.slf4j.Slf4j;
import org.ehcache.config.builders.CacheManagerBuilder;
import org.ehcache.jsr107.Eh107Configuration;
import org.ehcache.xml.XmlConfiguration;
import org.springframework.boot.autoconfigure.cache.JCacheManagerCustomizer;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.net.URL;
@Configuration
@Slf4j
@EnableCaching
public class CacheConfig {
@Bean
public JCacheManagerCustomizer cacheManagerCustomizer() {
URL myUrl = getClass().getResource("/donation-cache-config.xml");
XmlConfiguration xmlConfig = new XmlConfiguration(myUrl);
org.ehcache.CacheManager myCacheManager = CacheManagerBuilder.newCacheManager(xmlConfig);
return cm -> {
myCacheManager.getRuntimeConfiguration().getCacheConfigurations().entrySet().forEach(cacheConfiguration -> {
javax.cache.configuration.Configuration<?, ?> jConfiguration = Eh107Configuration.fromEhcacheCacheConfiguration(cacheConfiguration.getValue());
log.info("Creating custom cache: " + cacheConfiguration.getKey());
cm.createCache(cacheConfiguration.getKey(), jConfiguration);
});
};
}
}

View File

@@ -12,6 +12,7 @@ import java.util.List;
public class DebraFeatureConfig implements FeatureConfig {
public static final String DEBRA_DONATION_NOTIFICATION_DELAY_CONFIG_KEY = "debraDonationNotificationDelayMillis";
public static final String DEBRA_DONATION_API_FETCH_SIZE_KEY = "debraDonationApiFetchSize";
public static final String DEBRA_DONATION_NOTIFICATION_SERVER_ID_ENV_NAME = "DEBRA_DONATION_NOTIFICATION_SERVER_ID";
@Override
public FeatureDefinition getFeature() {
@@ -25,6 +26,6 @@ public class DebraFeatureConfig implements FeatureConfig {
@Override
public List<String> getRequiredSystemConfigKeys() {
return Arrays.asList(DEBRA_DONATION_NOTIFICATION_DELAY_CONFIG_KEY);
return Arrays.asList(DEBRA_DONATION_NOTIFICATION_DELAY_CONFIG_KEY, DEBRA_DONATION_API_FETCH_SIZE_KEY);
}
}

View File

@@ -11,5 +11,5 @@ import org.springframework.context.annotation.Configuration;
@ConfigurationProperties(prefix = "sissi.debra")
public class DebraProperties {
private String websocketURL;
private String donationsPageURL;
private String donationAPIUrl;
}

View File

@@ -2,4 +2,5 @@ package dev.sheldan.sissi.module.debra.config;
public class DebraSlashCommandNames {
public static final String DEBRA = "debra";
public static final String DEBRA_INTERNAL = "debrainternal";
}

View File

@@ -0,0 +1,28 @@
package dev.sheldan.sissi.module.debra.converter;
import dev.sheldan.sissi.module.debra.model.api.Donation;
import dev.sheldan.sissi.module.debra.model.api.DonationsResponse;
import dev.sheldan.sissi.module.debra.model.commands.DonationItemModel;
import dev.sheldan.sissi.module.debra.model.commands.DonationsModel;
import org.apache.commons.lang3.BooleanUtils;
import org.springframework.stereotype.Component;
@Component
public class DonationConverter {
public DonationItemModel convertDonation(Donation donation) {
return DonationItemModel
.builder()
.donationAmount(donation.getAmount())
.firstName(donation.getFirstname())
.lastName(donation.getLastname())
.anonymous(BooleanUtils.toBoolean(donation.getAnonym()))
.build();
}
public DonationsModel convertDonationResponse(DonationsResponse response) {
return DonationsModel
.builder()
.totalAmount(response.getPage().getCollected())
.build();
}
}

View File

@@ -0,0 +1,71 @@
package dev.sheldan.sissi.module.debra.listener;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.config.ListenerPriority;
import dev.sheldan.abstracto.core.interaction.InteractionService;
import dev.sheldan.abstracto.core.interaction.button.listener.ButtonClickedListener;
import dev.sheldan.abstracto.core.interaction.button.listener.ButtonClickedListenerModel;
import dev.sheldan.abstracto.core.interaction.button.listener.ButtonClickedListenerResult;
import dev.sheldan.abstracto.core.service.MessageService;
import dev.sheldan.abstracto.core.templating.model.MessageToSend;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import dev.sheldan.abstracto.core.utils.FutureUtils;
import dev.sheldan.sissi.module.debra.config.DebraFeatureDefinition;
import dev.sheldan.sissi.module.debra.service.DonationService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
@Component
@Slf4j
public class DebraInfoButtonClickedListener implements ButtonClickedListener {
@Autowired
private MessageService messageService;
@Autowired
private TemplateService templateService;
@Autowired
private InteractionService interactionService;
private static final String DEBRA_INFO_MESSAGE_TEMPLATE_KEY = "debraInfoMessage";
private static final String DEBRA_INFO_MESSAGE_RESPONSE_TEMPLATE_KEY = "debraInfoMessage_response";
@Override
public ButtonClickedListenerResult execute(ButtonClickedListenerModel model) {
MessageToSend messageToSend = templateService.renderEmbedTemplate(DEBRA_INFO_MESSAGE_TEMPLATE_KEY, new Object(), model.getServerId());
messageService.sendMessageToSendToUser(model.getEvent().getUser(), messageToSend).thenAccept(interactionHook -> {
log.info("Send debra info message to user {}", model.getEvent().getUser().getIdLong());
}).exceptionally(throwable -> {
log.error("Failed to send debra info message to user {}", model.getEvent().getUser().getIdLong(), throwable);
return null;
});
MessageToSend responseMessageToSend = templateService.renderEmbedTemplate(DEBRA_INFO_MESSAGE_RESPONSE_TEMPLATE_KEY, new Object(), model.getServerId());
FutureUtils.toSingleFutureGeneric(interactionService.sendMessageToInteraction(responseMessageToSend, model.getEvent().getInteraction().getHook()))
.thenAccept(interactionHook -> {
log.info("Send debra info message response to user {}", model.getEvent().getUser().getIdLong());
}).exceptionally(throwable -> {
log.error("Failed to send debra info message response to user {}", model.getEvent().getUser().getIdLong(), throwable);
return null;
});
return ButtonClickedListenerResult.ACKNOWLEDGED;
}
@Override
public Boolean handlesEvent(ButtonClickedListenerModel model) {
return model.getOrigin().equals(DonationService.DEBRA_INFO_BUTTON_ORIGIN);
}
@Override
public FeatureDefinition getFeature() {
return DebraFeatureDefinition.DEBRA;
}
@Override
public Integer getPriority() {
return ListenerPriority.MEDIUM;
}
}

View File

@@ -3,7 +3,7 @@ package dev.sheldan.sissi.module.debra.listener;
import dev.sheldan.abstracto.core.listener.AsyncStartupListener;
import dev.sheldan.abstracto.core.service.ConfigService;
import dev.sheldan.sissi.module.debra.config.DebraProperties;
import dev.sheldan.sissi.module.debra.model.Donation;
import dev.sheldan.sissi.module.debra.model.listener.DonationResponseModel;
import dev.sheldan.sissi.module.debra.service.DonationService;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
@@ -49,7 +49,7 @@ public class WebsocketListener extends WebSocketListener implements AsyncStartup
log.info("Waiting {} milli seconds to send notification.", delayMillis);
Thread.sleep(delayMillis);
log.info("Loading new donation amount and sending notification.");
Donation donation = donationService.parseDonationFromMessage(text);
DonationResponseModel donation = donationService.parseDonationFromMessage(text);
donationService.sendDonationNotification(donation).thenAccept(unused -> {
log.info("Successfully notified about donation.");
}).exceptionally(throwable -> {
@@ -65,13 +65,10 @@ public class WebsocketListener extends WebSocketListener implements AsyncStartup
@Override
public void onFailure(WebSocket webSocket, Throwable t, @Nullable Response response) {
log.warn("Websocket connection failed...", t);
webSocket.close(1000, null);
webSocket.close(1000, "Connection closed");
}
@Override
public void onClosing(WebSocket webSocket, int code, String reason) {
webSocket.close(1000, null);
log.info("Closing websocket connection. It was closed with code {} and reason {}.", code, reason);
}

View File

@@ -0,0 +1,20 @@
package dev.sheldan.sissi.module.debra.model.api;
import lombok.Builder;
import lombok.Getter;
import java.math.BigDecimal;
import java.math.BigInteger;
@Builder
@Getter
public class CampaignInfo {
private BigInteger donationCount;
private BigDecimal collected;
private BigDecimal target;
private String currency;
private String slug;
private String displayName;
private BigDecimal collectedNet;
private BigDecimal percent;
}

View File

@@ -0,0 +1,28 @@
package dev.sheldan.sissi.module.debra.model.api;
import com.google.gson.annotations.SerializedName;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import java.math.BigDecimal;
@Getter
@Setter
@Builder
public class Description {
@SerializedName("collected")
private BigDecimal collected;
@SerializedName("target")
private BigDecimal target;
@SerializedName("currency")
private String currency;
@SerializedName("slug")
private String slug;
@SerializedName("displayname")
private String displayName;
@SerializedName("collectednet")
private BigDecimal collectedNet;
@SerializedName("percent")
private BigDecimal percent;
}

View File

@@ -0,0 +1,26 @@
package dev.sheldan.sissi.module.debra.model.api;
import com.google.gson.annotations.SerializedName;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import java.math.BigDecimal;
@Getter
@Setter
@Builder
public class Donation {
@SerializedName("amount")
private BigDecimal amount;
@SerializedName("currency")
private String currency;
@SerializedName("text")
private String text;
@SerializedName("anonym")
private Integer anonym;
@SerializedName("firstname")
private String firstname;
@SerializedName("lastname")
private String lastname;
}

View File

@@ -0,0 +1,26 @@
package dev.sheldan.sissi.module.debra.model.api;
import dev.sheldan.sissi.module.debra.model.commands.DonationItemModel;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import java.math.BigDecimal;
@Getter
@Setter
@Builder
public class DonationInfo {
private String firstName;
private BigDecimal donationAmount;
private Boolean anonymous;
public static DonationInfo fromDonationItemModel(DonationItemModel donationItemModel) {
return DonationInfo
.builder()
.donationAmount(donationItemModel.getDonationAmount())
.anonymous(donationItemModel.getAnonymous())
.firstName(donationItemModel.getFirstName())
.build();
}
}

View File

@@ -0,0 +1,14 @@
package dev.sheldan.sissi.module.debra.model.api;
import lombok.Builder;
import lombok.Getter;
import java.math.BigDecimal;
import java.util.List;
@Getter
@Builder
public class DonationStats {
private List<DonationInfo> donations;
private BigDecimal totalAmount;
}

View File

@@ -0,0 +1,21 @@
package dev.sheldan.sissi.module.debra.model.api;
import com.google.gson.annotations.SerializedName;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import java.math.BigInteger;
import java.util.List;
@Getter
@Setter
@Builder
public class DonationsResponse {
@SerializedName("page")
private Description page;
@SerializedName("donation_count")
private BigInteger donationCount;
@SerializedName("donations")
private List<Donation> donations;
}

View File

@@ -0,0 +1,12 @@
package dev.sheldan.sissi.module.debra.model.commands;
import dev.sheldan.abstracto.core.interaction.button.ButtonPayload;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
@Builder
@Getter
@Setter
public class DebraInfoButtonPayload implements ButtonPayload {
}

View File

@@ -0,0 +1,10 @@
package dev.sheldan.sissi.module.debra.model.commands;
import lombok.Builder;
import lombok.Getter;
@Getter
@Builder
public class DebraInfoModel {
private String buttonId;
}

View File

@@ -0,0 +1,17 @@
package dev.sheldan.sissi.module.debra.model.commands;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import java.math.BigDecimal;
@Getter
@Setter
@Builder
public class DonationItemModel {
private String firstName;
private String lastName;
private BigDecimal donationAmount;
private Boolean anonymous;
}

View File

@@ -2,11 +2,21 @@ package dev.sheldan.sissi.module.debra.model.commands;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
import java.math.BigDecimal;
import java.util.List;
@Getter
@Builder
@Setter
public class DonationsModel {
private BigDecimal donationAmount;
private BigDecimal totalAmount;
private DonationType type;
private List<DonationItemModel> donations;
public enum DonationType {
LATEST, TOP
}
}

View File

@@ -1,6 +1,5 @@
package dev.sheldan.sissi.module.debra.model.listener;
import dev.sheldan.sissi.module.debra.model.Donation;
import lombok.Builder;
import lombok.Getter;
@@ -9,6 +8,6 @@ import java.math.BigDecimal;
@Getter
@Builder
public class DonationNotificationModel {
private Donation donation;
private DonationResponseModel donation;
private BigDecimal totalDonationAmount;
}

View File

@@ -1,4 +1,4 @@
package dev.sheldan.sissi.module.debra.model;
package dev.sheldan.sissi.module.debra.model.listener;
import lombok.Builder;
import lombok.Getter;
@@ -9,7 +9,7 @@ import java.math.BigDecimal;
@Getter
@Builder
@ToString
public class Donation {
public class DonationResponseModel {
private String donatorName;
private BigDecimal amount;
private String message;

View File

@@ -0,0 +1,22 @@
package dev.sheldan.sissi.module.debra.service;
import com.google.gson.JsonDeserializationContext;
import com.google.gson.JsonDeserializer;
import com.google.gson.JsonElement;
import com.google.gson.JsonParseException;
import java.lang.reflect.Type;
import java.math.BigDecimal;
public class BigDecimalGsonAdapter implements JsonDeserializer<BigDecimal> {
@Override
public BigDecimal deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
try {
return new BigDecimal(json.getAsString()
.replace(".", "")
.replace(',', '.'));
} catch (NumberFormatException e) {
throw new JsonParseException(e);
}
}
}

View File

@@ -0,0 +1,18 @@
package dev.sheldan.sissi.module.debra.service;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.CacheManager;
import org.springframework.stereotype.Component;
@Component
public class DonationCacheService implements InitializingBean {
@Autowired
private CacheManager cacheManager;
@Override
public void afterPropertiesSet() throws Exception {
cacheManager.getCache("donations");
}
}

View File

@@ -1,31 +1,52 @@
package dev.sheldan.sissi.module.debra.service;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException;
import dev.sheldan.abstracto.core.interaction.ComponentPayloadService;
import dev.sheldan.abstracto.core.interaction.ComponentService;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.service.ChannelService;
import dev.sheldan.abstracto.core.service.ConfigService;
import dev.sheldan.abstracto.core.service.PostTargetService;
import dev.sheldan.abstracto.core.service.management.ServerManagementService;
import dev.sheldan.abstracto.core.templating.model.MessageToSend;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import dev.sheldan.abstracto.core.utils.FutureUtils;
import dev.sheldan.sissi.module.debra.DonationAmountNotFoundException;
import dev.sheldan.sissi.module.debra.config.DebraPostTarget;
import dev.sheldan.sissi.module.debra.config.DebraProperties;
import dev.sheldan.sissi.module.debra.model.Donation;
import dev.sheldan.sissi.module.debra.converter.DonationConverter;
import dev.sheldan.sissi.module.debra.model.api.Donation;
import dev.sheldan.sissi.module.debra.model.api.DonationsResponse;
import dev.sheldan.sissi.module.debra.model.commands.DebraInfoButtonPayload;
import dev.sheldan.sissi.module.debra.model.commands.DebraInfoModel;
import dev.sheldan.sissi.module.debra.model.commands.DonationItemModel;
import dev.sheldan.sissi.module.debra.model.commands.DonationsModel;
import dev.sheldan.sissi.module.debra.model.listener.DonationResponseModel;
import dev.sheldan.sissi.module.debra.model.listener.DonationNotificationModel;
import lombok.extern.slf4j.Slf4j;
import net.dv8tion.jda.api.entities.Message;
import net.dv8tion.jda.api.entities.channel.middleman.GuildMessageChannel;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.math.BigDecimal;
import java.net.URL;
import java.util.Comparator;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import static dev.sheldan.sissi.module.debra.config.DebraFeatureConfig.DEBRA_DONATION_API_FETCH_SIZE_KEY;
import static dev.sheldan.sissi.module.debra.config.DebraFeatureConfig.DEBRA_DONATION_NOTIFICATION_SERVER_ID_ENV_NAME;
@Component
@@ -41,19 +62,45 @@ public class DonationService {
@Autowired
private TemplateService templateService;
@Autowired
private OkHttpClient okHttpClient;
@Autowired
private DonationConverter donationConverter;
@Autowired
private ConfigService configService;
@Autowired
private ChannelService channelService;
@Autowired
private ComponentService componentService;
@Autowired
private ComponentPayloadService componentPayloadService;
@Autowired
private ServerManagementService serverManagementService;
@Autowired
private DonationService self;
private static final String DEBRA_DONATION_NOTIFICATION_TEMPLATE_KEY = "debra_donation_notification";
private static final Pattern MESSAGE_PATTERN = Pattern.compile("(.*) hat (\\d{1,9},\\d{2}) Euro gespendet!<br \\/>Vielen Dank!<br \\/>Nachricht:<br \\/>(.*)");
private static final Pattern DONATION_PAGE_AMOUNT_PARTNER = Pattern.compile("\"metric4\",\\s*\"(.*)\"");
public Donation parseDonationFromMessage(String message) {
private static final String DEBRA_INFO_BUTTON_MESSAGE_TEMPLATE_KEY = "debraInfoButton";
public static final String DEBRA_INFO_BUTTON_ORIGIN = "DEBRA_INFO_BUTTON";
public DonationResponseModel parseDonationFromMessage(String message) {
Matcher matcher = MESSAGE_PATTERN.matcher(message);
if (matcher.find()) {
String donatorName = matcher.group(1);
String amountString = matcher.group(2);
BigDecimal amount = new BigDecimal(amountString.replace(',', '.'));
String donationMessage = Optional.ofNullable(matcher.group(3)).map(msg -> msg.replaceAll("(<br>)+", " ")).map(String::trim).orElse("");
return Donation
return DonationResponseModel
.builder()
.message(donationMessage)
.donatorName(donatorName)
@@ -64,35 +111,101 @@ public class DonationService {
}
}
public BigDecimal fetchCurrentDonationAmount() {
try (InputStream is = new URL(debraProperties.getDonationsPageURL()).openStream()) {
BufferedReader br = new BufferedReader(new InputStreamReader(is));
String line;
while ((line = br.readLine()) != null) {
Matcher matcher = DONATION_PAGE_AMOUNT_PARTNER.matcher(line);
if (matcher.find()) {
return new BigDecimal(matcher.group(1).replace(',', '.'));
}
}
log.warn("Did not find the donation amount in the configured URL {}", debraProperties.getDonationsPageURL());
throw new DonationAmountNotFoundException();
} catch (IOException ex) {
log.warn("Failed to load page for parsing donation amount {}.", debraProperties.getDonationsPageURL(), ex);
throw new DonationAmountNotFoundException();
}
public List<DonationItemModel> getHighestDonations(DonationsResponse response, Integer maxCount) {
List<Donation> topDonations = response
.getDonations()
.stream()
.sorted(Comparator.comparing(Donation::getAmount)
.reversed())
.collect(Collectors.toList());
return topDonations
.stream()
.limit(maxCount)
.map(donation -> donationConverter.convertDonation(donation))
.collect(Collectors.toList());
}
public CompletableFuture<Void> sendDonationNotification(Donation donation) {
public List<DonationItemModel> getLatestDonations(DonationsResponse response, Integer maxCount) {
return response
.getDonations()
.stream()
.limit(maxCount)
.map(donation -> donationConverter.convertDonation(donation))
.collect(Collectors.toList());
}
@Cacheable(value = "donation-cache")
public DonationsResponse getCachedDonationAmount(Long serverId) {
return fetchCurrentDonationAmount(serverId);
}
public DonationsResponse fetchCurrentDonationAmount(Long serverId) {
try {
Long fetchSize = configService.getLongValueOrConfigDefault(DEBRA_DONATION_API_FETCH_SIZE_KEY, serverId);
Request request = new Request.Builder()
.url(String.format(debraProperties.getDonationAPIUrl(), fetchSize))
.get()
.build();
Response response = okHttpClient.newCall(request).execute();
if(!response.isSuccessful()) {
if (log.isDebugEnabled()) {
log.error("Failed to retrieve donation response. Response had code {} with body {}.",
response.code(), response.body());
}
throw new DonationAmountNotFoundException();
}
Gson gson = getGson();
return gson.fromJson(response.body().string(), DonationsResponse.class);
} catch (Exception exception) {
throw new AbstractoRunTimeException(exception);
}
}
private Gson getGson() {
return new GsonBuilder()
.registerTypeAdapter(BigDecimal.class, new BigDecimalGsonAdapter())
.create();
}
private DonationsModel getDonationInfoModel(Long serverId) {
return donationConverter.convertDonationResponse(fetchCurrentDonationAmount(serverId));
}
public CompletableFuture<Void> sendDonationNotification(DonationResponseModel donation) throws IOException {
Long targetServerId = Long.parseLong(System.getenv(DEBRA_DONATION_NOTIFICATION_SERVER_ID_ENV_NAME));
DonationsModel donationInfoModel = getDonationInfoModel(targetServerId);
DonationNotificationModel model = DonationNotificationModel
.builder()
.donation(donation)
.totalDonationAmount(fetchCurrentDonationAmount())
.totalDonationAmount(donationInfoModel.getTotalAmount())
.build();
MessageToSend messageToSend = templateService.renderEmbedTemplate(DEBRA_DONATION_NOTIFICATION_TEMPLATE_KEY, model);
Long targetServerId = Long.parseLong(System.getenv(DEBRA_DONATION_NOTIFICATION_SERVER_ID_ENV_NAME));
List<CompletableFuture<Message>> firstMessage = postTargetService.sendEmbedInPostTarget(messageToSend, DebraPostTarget.DEBRA_DONATION_NOTIFICATION, targetServerId);
List<CompletableFuture<Message>> secondMessage = postTargetService.sendEmbedInPostTarget(messageToSend, DebraPostTarget.DEBRA_DONATION_NOTIFICATION2, targetServerId);
firstMessage.addAll(secondMessage);
return FutureUtils.toSingleFutureGeneric(firstMessage);
}
public CompletableFuture<Void> sendDebraInfoButtonMessage(GuildMessageChannel guildMessageChannel) {
String buttonId = componentService.generateComponentId();
DebraInfoModel model = DebraInfoModel
.builder()
.buttonId(buttonId)
.build();
MessageToSend messageToSend = templateService.renderEmbedTemplate(DEBRA_INFO_BUTTON_MESSAGE_TEMPLATE_KEY, model, guildMessageChannel.getGuild().getIdLong());
return FutureUtils.toSingleFutureGeneric(channelService.sendMessageToSendToChannel(messageToSend, guildMessageChannel)).thenAccept(unused -> {
self.persistButtonPayload(guildMessageChannel, buttonId);
});
}
@Transactional
public void persistButtonPayload(GuildMessageChannel guildMessageChannel, String buttonId) {
DebraInfoButtonPayload payload = DebraInfoButtonPayload
.builder()
.build();
AServer server = serverManagementService.loadServer(guildMessageChannel.getGuild().getIdLong());
componentPayloadService.createButtonPayload(buttonId, payload, DEBRA_INFO_BUTTON_ORIGIN, server);
}
}

View File

@@ -5,7 +5,10 @@ abstracto.postTargets.debraDonationNotification.name=debraDonationNotification
abstracto.postTargets.debraDonationNotification2.name=debraDonationNotification2
sissi.debra.websocketURL=ws://spenden.baba.fm:8765/
sissi.debra.donationsPageURL=https://em.altruja.de/discord-fuer-debra-2022
sissi.debra.donationAPIUrl=https://www.altruja.de/api/page/discord-gg-austria-fuer-debra-2023?details=1&num=%s&ort=0
abstracto.systemConfigs.debraDonationNotificationDelayMillis.name=debraDonationNotificationDelayMillis
abstracto.systemConfigs.debraDonationNotificationDelayMillis.longValue=60000
abstracto.systemConfigs.debraDonationNotificationDelayMillis.longValue=60000
abstracto.systemConfigs.debraDonationApiFetchSize.name=debraDonationApiFetchSize
abstracto.systemConfigs.debraDonationApiFetchSize.longValue=1000

View File

@@ -0,0 +1,24 @@
<config
xmlns:xsi='http://www.w3.org/2001/XMLSchema-instance'
xmlns='http://www.ehcache.org/v3'
xsi:schemaLocation="
http://www.ehcache.org/v3
http://www.ehcache.org/schema/ehcache-core-3.8-1.xsd">
<cache uses-template="default" alias="donation-cache">
<expiry>
<ttl unit="seconds">120</ttl>
</expiry>
<resources>
<heap unit="entries">5</heap>
</resources>
</cache>
<cache-template name="default">
<expiry>
<ttl unit="seconds">600</ttl>
</expiry>
<resources>
<heap>50</heap>
</resources>
</cache-template>
</config>

View File

@@ -0,0 +1,10 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog dbchangelog.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext dbchangelog.xsd
http://www.liquibase.org/xml/ns/pro dbchangelog.xsd" >
<include file="seedData/data.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>

View File

@@ -0,0 +1,20 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog dbchangelog.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext dbchangelog.xsd
http://www.liquibase.org/xml/ns/pro dbchangelog.xsd" >
<property name="utilityModule" value="(SELECT id FROM module WHERE name = 'utility')"/>
<property name="debraFeature" value="(SELECT id FROM feature WHERE key = 'debra')"/>
<changeSet author="Sheldan" id="debraInfoButton-command">
<insert tableName="command">
<column name="name" value="debraInfoButton"/>
<column name="module_id" valueComputed="${utilityModule}"/>
<column name="feature_id" valueComputed="${debraFeature}"/>
</insert>
</changeSet>
</databaseChangeLog>

View File

@@ -0,0 +1,10 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog dbchangelog.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext dbchangelog.xsd
http://www.liquibase.org/xml/ns/pro dbchangelog.xsd" >
<include file="command.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>

View File

@@ -7,4 +7,5 @@
http://www.liquibase.org/xml/ns/dbchangelog-ext dbchangelog.xsd
http://www.liquibase.org/xml/ns/pro dbchangelog.xsd" >
<include file="1.3.6/collection.xml" relativeToChangelogFile="true"/>
<include file="1.4.21/collection.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>

View File

@@ -3,7 +3,7 @@
<parent>
<groupId>dev.sheldan.sissi.application</groupId>
<artifactId>sissi-modules</artifactId>
<version>1.3.14</version>
<version>1.4.24-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -0,0 +1,158 @@
package dev.sheldan.sissi.module.meetup.commands;
import dev.sheldan.abstracto.core.command.UtilityModuleDefinition;
import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand;
import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
import dev.sheldan.abstracto.core.command.config.HelpInfo;
import dev.sheldan.abstracto.core.command.config.Parameter;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.interaction.InteractionService;
import dev.sheldan.abstracto.core.interaction.slash.SlashCommandConfig;
import dev.sheldan.abstracto.core.interaction.slash.parameter.SlashCommandParameterService;
import dev.sheldan.sissi.module.meetup.config.MeetupFeatureDefinition;
import dev.sheldan.sissi.module.meetup.config.MeetupSlashCommandNames;
import dev.sheldan.sissi.module.meetup.exception.NotMeetupOrganizerException;
import dev.sheldan.sissi.module.meetup.model.database.Meetup;
import dev.sheldan.sissi.module.meetup.service.MeetupServiceBean;
import dev.sheldan.sissi.module.meetup.service.management.MeetupManagementServiceBean;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
@Component
public class ChangeMeetup extends AbstractConditionableCommand {
private static final String CHANGE_MEETUP_COMMAND = "changeMeetup";
private static final String MEETUP_ID_PARAMETER = "meetupId";
private static final String MEETUP_NEW_VALUE_PARAMETER = "newValue";
private static final String MEETUP_PROPERTY_PARAMETER = "property";
@Autowired
private SlashCommandParameterService slashCommandParameterService;
@Autowired
private MeetupManagementServiceBean meetupManagementServiceBean;
@Autowired
private MeetupServiceBean meetupServiceBean;
@Autowired
private InteractionService interactionService;
@Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
List<Object> parameters = commandContext.getParameters().getParameters();
Long meetupId = (Long) parameters.get(0);
Meetup meetup = meetupManagementServiceBean.getMeetup(meetupId, commandContext.getGuild().getIdLong());
if(!meetup.getOrganizer().getUserReference().getId().equals(commandContext.getAuthor().getIdLong())) {
throw new NotMeetupOrganizerException();
}
String property = (String) parameters.get(1);
MeetupProperty propertyEnum = MeetupProperty.valueOf(property);
String newValue = (String) parameters.get(2);
return updateMeetup(meetup, propertyEnum, newValue).thenApply(unused -> CommandResult.fromSuccess());
}
private CompletableFuture<Void> updateMeetup(Meetup meetup, MeetupProperty propertyEnum, String newValue) {
CompletableFuture<Void> future;
switch (propertyEnum) {
case TOPIC:
future = meetupServiceBean.changeMeetupTopic(meetup, newValue);
break;
case LOCATION:
future = meetupServiceBean.changeMeetupLocation(meetup, newValue);
break;
default:
case DESCRIPTION:
future = meetupServiceBean.changeMeetupDescription(meetup, newValue);
break;
}
return future;
}
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
Long meetupId = slashCommandParameterService.getCommandOption(MEETUP_ID_PARAMETER, event, Integer.class).longValue();
Meetup meetup = meetupManagementServiceBean.getMeetup(meetupId, event.getGuild().getIdLong());
if(!meetup.getOrganizer().getUserReference().getId().equals(event.getMember().getIdLong())) {
throw new NotMeetupOrganizerException();
}
String newValue = slashCommandParameterService.getCommandOption(MEETUP_NEW_VALUE_PARAMETER, event, String.class);
String property = slashCommandParameterService.getCommandOption(MEETUP_PROPERTY_PARAMETER, event, String.class);
MeetupProperty propertyEnum = MeetupProperty.valueOf(property);
return updateMeetup(meetup, propertyEnum, newValue)
.thenCompose(commandResult -> interactionService.replyEmbed("changeMeetup_response", event))
.thenApply(unused -> CommandResult.fromSuccess());
}
@Override
public CommandConfiguration getConfiguration() {
Parameter meetupIdParameter = Parameter
.builder()
.templated(true)
.name(MEETUP_ID_PARAMETER)
.type(Long.class)
.build();
List<String> meetupProperties = Arrays
.stream(MeetupProperty.values())
.map(Enum::name)
.collect(Collectors.toList());
Parameter meetupPropertyParameter = Parameter
.builder()
.templated(true)
.name(MEETUP_PROPERTY_PARAMETER)
.type(String.class)
.choices(meetupProperties)
.build();
Parameter newValueParameter = Parameter
.builder()
.templated(true)
.name(MEETUP_NEW_VALUE_PARAMETER)
.type(String.class)
.build();
List<Parameter> parameters = Arrays.asList(meetupIdParameter, meetupPropertyParameter, newValueParameter);
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.build();
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(MeetupSlashCommandNames.MEETUP)
.commandName("changeMeetup")
.build();
return CommandConfiguration.builder()
.name(CHANGE_MEETUP_COMMAND)
.module(UtilityModuleDefinition.UTILITY)
.templated(true)
.async(true)
.slashCommandConfig(slashCommandConfig)
.supportsEmbedException(true)
.causesReaction(true)
.parameters(parameters)
.help(helpInfo)
.build();
}
@Override
public FeatureDefinition getFeature() {
return MeetupFeatureDefinition.MEETUP;
}
public enum MeetupProperty {
DESCRIPTION, TOPIC, LOCATION
}
}

View File

@@ -5,6 +5,7 @@ import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand
import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
import dev.sheldan.abstracto.core.command.config.HelpInfo;
import dev.sheldan.abstracto.core.command.config.Parameter;
import dev.sheldan.abstracto.core.command.config.validator.MaxStringLengthValidator;
import dev.sheldan.abstracto.core.command.execution.CommandContext;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
@@ -71,7 +72,9 @@ public class CreateMeetup extends AbstractConditionableCommand {
private static final String MEETUP_TIME_PARAMETER = "meetupTime";
private static final String TOPIC_PARAMETER = "topic";
private static final String DESCRIPTION_PARAMETER = "description";
private static final String LOCATION_PARAMETER = "location";
private static final String CONFIRMATION_TEMPLATE = "createMeetup_confirmation";
private static final String DEFAULT_LOCATION_STRING = "\"\"";
@Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
@@ -86,7 +89,7 @@ public class CreateMeetup extends AbstractConditionableCommand {
}
AUserInAServer organizer = userInServerManagementService.loadOrCreateUser(commandContext.getAuthor());
AChannel meetupChannel = channelManagementService.loadChannel(commandContext.getChannel().getIdLong());
Meetup meetup = meetupManagementServiceBean.createMeetup(meetupTime, meetupTopic, description, organizer, meetupChannel);
Meetup meetup = meetupManagementServiceBean.createMeetup(meetupTime, meetupTopic, description, organizer, meetupChannel, DEFAULT_LOCATION_STRING);
String confirmationId = componentService.generateComponentId();
String cancelId = componentService.generateComponentId();
MeetupConfirmationModel model = MeetupConfirmationModel
@@ -95,6 +98,7 @@ public class CreateMeetup extends AbstractConditionableCommand {
.guildId(commandContext.getGuild().getIdLong())
.description(description)
.topic(meetupTopic)
.location(meetup.getLocation())
.confirmationId(confirmationId)
.cancelId(cancelId)
.meetupId(meetup.getId().getId())
@@ -117,10 +121,17 @@ public class CreateMeetup extends AbstractConditionableCommand {
} else {
description = "";
}
String location;
if(slashCommandParameterService.hasCommandOption(LOCATION_PARAMETER, event)) {
location = slashCommandParameterService.getCommandOption(LOCATION_PARAMETER, event, String.class);
} else {
location = DEFAULT_LOCATION_STRING;
}
Instant meetupTime = Instant.ofEpochSecond(time);
AUserInAServer organizer = userInServerManagementService.loadOrCreateUser(event.getMember());
AChannel meetupChannel = channelManagementService.loadChannel(event.getChannel().getIdLong());
Meetup meetup = meetupManagementServiceBean.createMeetup(meetupTime, topic, description, organizer, meetupChannel);
Meetup meetup = meetupManagementServiceBean.createMeetup(meetupTime, topic, description, organizer, meetupChannel, location);
String confirmationId = componentService.generateComponentId();
String cancelId = componentService.generateComponentId();
MeetupConfirmationModel model = MeetupConfirmationModel
@@ -129,6 +140,7 @@ public class CreateMeetup extends AbstractConditionableCommand {
.guildId(event.getGuild().getIdLong())
.description(description)
.topic(topic)
.location(meetup.getLocation())
.confirmationId(confirmationId)
.cancelId(cancelId)
.meetupId(meetup.getId().getId())
@@ -166,7 +178,17 @@ public class CreateMeetup extends AbstractConditionableCommand {
.type(String.class)
.build();
List<Parameter> parameters = Arrays.asList(timeParameter, topicParameter, descriptionParameter);
Parameter locationParameter = Parameter
.builder()
.templated(true)
.name(LOCATION_PARAMETER)
.remainder(true)
.optional(true)
.slashCommandOnly(true)
.type(String.class)
.build();
List<Parameter> parameters = Arrays.asList(timeParameter, topicParameter, descriptionParameter, locationParameter);
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)

View File

@@ -49,19 +49,19 @@ public class ListMeetups extends AbstractConditionableCommand {
@Override
public CompletableFuture<CommandResult> executeAsync(CommandContext commandContext) {
MessageToSend messageToSend = getMessageToSend(commandContext.getGuild().getIdLong());
MessageToSend messageToSend = getMessageToSend(commandContext.getGuild().getIdLong(), commandContext.getChannel().getIdLong());
return FutureUtils.toSingleFutureGeneric(channelService.sendMessageToSendToChannel(messageToSend, commandContext.getChannel()))
.thenApply(unused -> CommandResult.fromIgnored());
}
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
return interactionService.replyMessageToSend(getMessageToSend(event.getGuild().getIdLong()), event)
return interactionService.replyMessageToSend(getMessageToSend(event.getGuild().getIdLong(), event.getChannel().getIdLong()), event)
.thenApply(interactionHook -> CommandResult.fromSuccess());
}
private MessageToSend getMessageToSend(Long serverId) {
List<Meetup> meetups = meetupManagementServiceBean.getIncomingMeetups();
private MessageToSend getMessageToSend(Long serverId, Long channelId) {
List<Meetup> meetups = meetupManagementServiceBean.getIncomingMeetups(serverId, channelId);
List<MeetupListItemModel> listItems = meetups
.stream()
.map(MeetupListItemModel::fromMeetup)

View File

@@ -15,6 +15,7 @@ import dev.sheldan.sissi.module.meetup.config.MeetupFeatureDefinition;
import dev.sheldan.sissi.module.meetup.config.MeetupSlashCommandNames;
import dev.sheldan.sissi.module.meetup.exception.NotMeetupOrganizerException;
import dev.sheldan.sissi.module.meetup.model.database.Meetup;
import dev.sheldan.sissi.module.meetup.model.database.MeetupDecision;
import dev.sheldan.sissi.module.meetup.service.MeetupServiceBean;
import dev.sheldan.sissi.module.meetup.service.management.MeetupManagementServiceBean;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
@@ -24,6 +25,7 @@ import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;
@Component
public class NotifyMeetupParticipants extends AbstractConditionableCommand {
@@ -42,6 +44,7 @@ public class NotifyMeetupParticipants extends AbstractConditionableCommand {
private static final String MEETUP_ID_PARAMETER = "meetupId";
private static final String NOTIFICATION_MESSAGE_PARAMETER = "notificationMessage";
private static final String NOTIFICATION_MEETUP_DECISION = "decision";
private static final String NOTIFY_MEETUP_PARTICIPANTS_COMMAND = "notifyMeetupParticipants";
private static final String NOTIFY_MEETUP_PARTICIPANTS_RESPONSE = "notifyMeetupParticipants_response";
@@ -54,7 +57,7 @@ public class NotifyMeetupParticipants extends AbstractConditionableCommand {
throw new NotMeetupOrganizerException();
}
String notificationMessage = (String) parameters.get(1);
return meetupServiceBean.notifyMeetupParticipants(meetup, notificationMessage)
return meetupServiceBean.notifyMeetupParticipants(meetup, notificationMessage, null)
.thenApply(unused -> CommandResult.fromSuccess());
}
@@ -65,8 +68,12 @@ public class NotifyMeetupParticipants extends AbstractConditionableCommand {
if(!meetup.getOrganizer().getUserReference().getId().equals(event.getMember().getIdLong())) {
throw new NotMeetupOrganizerException();
}
MeetupDecision toNotify = null;
if(slashCommandParameterService.hasCommandOption(NOTIFICATION_MEETUP_DECISION, event)) {
toNotify = MeetupDecision.valueOf(slashCommandParameterService.getCommandOption(NOTIFICATION_MEETUP_DECISION, event, String.class));
}
String notificationMessage = slashCommandParameterService.getCommandOption(NOTIFICATION_MESSAGE_PARAMETER, event, String.class);
return meetupServiceBean.notifyMeetupParticipants(meetup, notificationMessage)
return meetupServiceBean.notifyMeetupParticipants(meetup, notificationMessage, toNotify)
.thenCompose(unused -> interactionService.replyEmbed(NOTIFY_MEETUP_PARTICIPANTS_RESPONSE, event))
.thenApply(unused -> CommandResult.fromSuccess());
}
@@ -88,7 +95,22 @@ public class NotifyMeetupParticipants extends AbstractConditionableCommand {
.remainder(true)
.build();
List<Parameter> parameters = Arrays.asList(meetupIdParameter, notificationMessage);
List<String> meetupDecisions = Arrays
.stream(MeetupDecision.values())
.map(Enum::name)
.collect(Collectors.toList());
Parameter meetupDecisionChoice = Parameter
.builder()
.templated(true)
.name(NOTIFICATION_MEETUP_DECISION)
.type(String.class)
.optional(true)
.choices(meetupDecisions)
.slashCommandOnly(true)
.build();
List<Parameter> parameters = Arrays.asList(meetupIdParameter, notificationMessage, meetupDecisionChoice);
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)

View File

@@ -2,8 +2,14 @@ package dev.sheldan.sissi.module.meetup.config;
import dev.sheldan.abstracto.core.config.FeatureConfig;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.config.FeatureMode;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
import static dev.sheldan.sissi.module.meetup.config.MeetupFeatureMode.ATTACH_ICS_FILE;
@Component
public class MeetupFeatureConfig implements FeatureConfig {
@Override
@@ -11,4 +17,8 @@ public class MeetupFeatureConfig implements FeatureConfig {
return MeetupFeatureDefinition.MEETUP;
}
@Override
public List<FeatureMode> getAvailableModes() {
return Arrays.asList(ATTACH_ICS_FILE);
}
}

View File

@@ -0,0 +1,15 @@
package dev.sheldan.sissi.module.meetup.config;
import dev.sheldan.abstracto.core.config.FeatureMode;
import lombok.Getter;
@Getter
public enum MeetupFeatureMode implements FeatureMode {
ATTACH_ICS_FILE("attachIcsFile");
private final String key;
MeetupFeatureMode(String key) {
this.key = key;
}
}

View File

@@ -16,7 +16,6 @@ import org.springframework.stereotype.Component;
@PersistJobDataAfterExecution
public class MeetupCleanupJob extends QuartzJobBean {
@Autowired
private MeetupServiceBean meetupServiceBean;

View File

@@ -16,6 +16,7 @@ import dev.sheldan.abstracto.core.service.MessageService;
import dev.sheldan.abstracto.core.service.management.ChannelManagementService;
import dev.sheldan.abstracto.core.service.management.ServerManagementService;
import dev.sheldan.abstracto.core.templating.model.MessageToSend;
import dev.sheldan.abstracto.core.utils.FileService;
import dev.sheldan.abstracto.core.utils.FutureUtils;
import dev.sheldan.sissi.module.meetup.config.MeetupFeatureDefinition;
import dev.sheldan.sissi.module.meetup.model.database.Meetup;
@@ -76,6 +77,9 @@ public class MeetupConfirmationListener implements ButtonClickedListener {
@Autowired
private MeetupComponentManagementServiceBean meetupComponentManagementServiceBean;
@Autowired
private FileService fileService;
@Override
public ButtonClickedListenerResult execute(ButtonClickedListenerModel model) {
MeetupConfirmationPayload payload = (MeetupConfirmationPayload) model.getDeserializedPayload();
@@ -104,6 +108,10 @@ public class MeetupConfirmationListener implements ButtonClickedListener {
messageModel.setNoId(noButtonId);
messageModel.setMaybeId(maybeButtonId);
messageModel.setNoTimeId(noTimeButtonId);
meetup.setYesButtonId(yesButtonId);
meetup.setMaybeButtonId(maybeButtonId);
meetup.setNoTimeButtonId(noTimeButtonId);
meetup.setNotInterestedButtonId(noButtonId);
messageModel.setCancelled(false);
Long meetupId = payload.getMeetupId();
Long serverId = payload.getGuildId();
@@ -111,6 +119,9 @@ public class MeetupConfirmationListener implements ButtonClickedListener {
List<CompletableFuture<Message>> messageFutures = channelService.sendMessageToSendToChannel(messageToSend, model.getEvent().getMessageChannel());
FutureUtils.toSingleFutureGeneric(messageFutures).thenAccept(unused -> {
messageService.deleteMessage(model.getEvent().getMessage());
messageToSend.getAttachedFiles().forEach(attachedFile -> {
fileService.safeDeleteIgnoreException(attachedFile.getFile());
});
Message meetupMessage = messageFutures.get(0).join();
messageService.pinMessage(meetupMessage);
self.persistPayloads(meetupId, serverId, yesButtonId, noButtonId, maybeButtonId, noTimeButtonId, meetupMessage);

View File

@@ -14,6 +14,7 @@ public class MeetupConfirmationModel {
private MemberDisplay organizer;
private Instant meetupTime;
private Long meetupId;
private String location;
private String topic;
private String description;
private Long guildId;

View File

@@ -6,7 +6,7 @@ import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import lombok.*;
import javax.persistence.*;
import jakarta.persistence.*;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
@@ -43,6 +43,26 @@ public class Meetup {
@Column(name = "message_id")
private Long messageId;
@Getter
@Column(name = "location")
private String location;
@Getter
@Column(name = "yes_button_id")
private String yesButtonId;
@Getter
@Column(name = "maybe_button_id")
private String maybeButtonId;
@Getter
@Column(name = "no_time_button_id")
private String noTimeButtonId;
@Getter
@Column(name = "not_interested_button_id")
private String notInterestedButtonId;
@OneToMany(
fetch = FetchType.LAZY,
orphanRemoval = true,

View File

@@ -4,7 +4,7 @@ import dev.sheldan.abstracto.core.models.database.ComponentPayload;
import dev.sheldan.sissi.module.meetup.model.database.embed.MeetupComponentId;
import lombok.*;
import javax.persistence.*;
import jakarta.persistence.*;
import java.time.Instant;
@Builder

View File

@@ -4,7 +4,7 @@ import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import dev.sheldan.sissi.module.meetup.model.database.embed.MeetupParticipationId;
import lombok.*;
import javax.persistence.*;
import jakarta.persistence.*;
import java.time.Instant;
@Builder

View File

@@ -2,8 +2,8 @@ package dev.sheldan.sissi.module.meetup.model.database.embed;
import lombok.*;
import javax.persistence.Column;
import javax.persistence.Embeddable;
import jakarta.persistence.Column;
import jakarta.persistence.Embeddable;
import java.io.Serializable;
@Embeddable

View File

@@ -2,8 +2,8 @@ package dev.sheldan.sissi.module.meetup.model.database.embed;
import lombok.*;
import javax.persistence.Column;
import javax.persistence.Embeddable;
import jakarta.persistence.Column;
import jakarta.persistence.Embeddable;
import java.io.Serializable;
@Embeddable

View File

@@ -0,0 +1,17 @@
package dev.sheldan.sissi.module.meetup.model.template;
import lombok.Builder;
import lombok.Getter;
import lombok.Setter;
@Getter
@Builder
@Setter
public class MeetupIcsModel {
private Boolean attachIcsFile;
private String iceFileName;
private String icsFormattedCreationTime;
private String icsFormattedStartTime;
private String icsFormattedEndTime;
}

View File

@@ -15,6 +15,8 @@ public class MeetupMessageModel {
private String topic;
private String description;
private Instant meetupTime;
private String location;
private String decodedLocation;
private MemberDisplay organizer;
private Long meetupId;
private String yesId;
@@ -26,4 +28,5 @@ public class MeetupMessageModel {
private List<MemberDisplay> maybeParticipants;
private List<MemberDisplay> noTimeParticipants;
private List<MemberDisplay> declinedParticipants;
private MeetupIcsModel meetupIcsModel;
}

View File

@@ -11,4 +11,7 @@ import java.util.List;
public class MeetupNotificationModel {
private List<MemberDisplay> participants;
private String notificationMessage;
private Long meetupMessageId;
private String meetupTopic;
private Long meetupId;
}

View File

@@ -12,7 +12,6 @@ import java.util.List;
@Repository
public interface MeetupRepository extends JpaRepository<Meetup, ServerSpecificId> {
List<Meetup> findByMeetupTimeLessThan(Instant date);
List<Meetup> findByMeetupTimeGreaterThan(Instant date);
List<Meetup> findByMeetupTimeGreaterThanAndState(Instant date, MeetupState state);
List<Meetup> findByMeetupTimeGreaterThanAndStateAndServer_IdAndMeetupChannel_Id(Instant date, MeetupState state, Long serverId, Long channelId);
List<Meetup> findByState(MeetupState state);
}

View File

@@ -1,5 +1,6 @@
package dev.sheldan.sissi.module.meetup.service;
import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException;
import dev.sheldan.abstracto.core.interaction.ComponentPayloadManagementService;
import dev.sheldan.abstracto.core.interaction.ComponentPayloadService;
import dev.sheldan.abstracto.core.models.ServerChannelMessage;
@@ -9,9 +10,12 @@ import dev.sheldan.abstracto.core.service.*;
import dev.sheldan.abstracto.core.service.management.ServerManagementService;
import dev.sheldan.abstracto.core.templating.model.MessageToSend;
import dev.sheldan.abstracto.core.templating.service.TemplateService;
import dev.sheldan.abstracto.core.utils.FileService;
import dev.sheldan.abstracto.core.utils.FutureUtils;
import dev.sheldan.abstracto.scheduling.model.JobParameters;
import dev.sheldan.abstracto.scheduling.service.SchedulerService;
import dev.sheldan.sissi.module.meetup.config.MeetupFeatureDefinition;
import dev.sheldan.sissi.module.meetup.config.MeetupFeatureMode;
import dev.sheldan.sissi.module.meetup.model.command.MeetupChangeTimeConfirmationModel;
import dev.sheldan.sissi.module.meetup.model.command.MeetupConfirmationModel;
import dev.sheldan.sissi.module.meetup.model.database.Meetup;
@@ -20,6 +24,7 @@ import dev.sheldan.sissi.module.meetup.model.database.MeetupParticipant;
import dev.sheldan.sissi.module.meetup.model.database.MeetupState;
import dev.sheldan.sissi.module.meetup.model.payload.MeetupChangeTimeConfirmationPayload;
import dev.sheldan.sissi.module.meetup.model.payload.MeetupConfirmationPayload;
import dev.sheldan.sissi.module.meetup.model.template.MeetupIcsModel;
import dev.sheldan.sissi.module.meetup.model.template.MeetupMessageModel;
import dev.sheldan.sissi.module.meetup.model.template.MeetupNotificationModel;
import dev.sheldan.sissi.module.meetup.model.template.MeetupTimeChangedNotificationModel;
@@ -33,7 +38,13 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.concurrent.CompletableFuture;
@@ -75,6 +86,9 @@ public class MeetupServiceBean {
@Autowired
private MeetupServiceBean self;
@Autowired
private FileService fileService;
@Autowired
private ChannelService channelService;
@@ -87,11 +101,16 @@ public class MeetupServiceBean {
@Autowired
private SchedulerService schedulerService;
@Autowired
private FeatureModeService featureModeService;
@Autowired
private MeetupParticipatorManagementServiceBean meetupParticipatorManagementServiceBean;
@Autowired
private MeetupComponentManagementServiceBean meetupComponentManagementServiceBean;
private static final String ICS_TIME_STAMP_FORMAT = "yMMdd'T'kkmmss'Z'";
private static final DateTimeFormatter ICS_DATE_TIME_FORMATTER = DateTimeFormatter.ofPattern(ICS_TIME_STAMP_FORMAT);
public void storeMeetupConfirmation(MeetupConfirmationModel model) {
AServer server = serverManagementService.loadServer(model.getGuildId());
@@ -122,6 +141,22 @@ public class MeetupServiceBean {
componentPayloadService.createButtonPayload(model.getCancelId(), confirmationPayload, MEETUP_CHANGE_TIME_CONFIRMATION_BUTTON, server);
}
private MeetupIcsModel getMeetupICSModel(Meetup meetup) {
ZonedDateTime startTime = meetup.getMeetupTime().atZone(ZoneId.of("UTC"));
ZonedDateTime endTime = meetup.getMeetupTime().plus(1, ChronoUnit.HOURS).atZone(ZoneId.of("UTC"));
String icsFormattedStartTime = startTime.format(ICS_DATE_TIME_FORMATTER);
String icsFormattedEndTime = endTime.format(ICS_DATE_TIME_FORMATTER);
String icsFormattedMeetupCreationTime = Instant.now().atZone(ZoneId.of("UTC"))
.format(ICS_DATE_TIME_FORMATTER);
boolean attachIcsFile = featureModeService.featureModeActive(MeetupFeatureDefinition.MEETUP, meetup.getServer().getId(), MeetupFeatureMode.ATTACH_ICS_FILE);
return MeetupIcsModel
.builder()
.attachIcsFile(attachIcsFile)
.icsFormattedCreationTime(icsFormattedMeetupCreationTime)
.icsFormattedStartTime(icsFormattedStartTime)
.icsFormattedEndTime(icsFormattedEndTime)
.build();
}
public MeetupMessageModel getMeetupMessageModel(Meetup meetup) {
List<MeetupParticipant> allParticipants = meetup.getParticipants();
@@ -141,10 +176,17 @@ public class MeetupServiceBean {
.stream()
.filter(meetupParticipator -> meetupParticipator.getDecision().equals(MeetupDecision.NO_TIME))
.collect(Collectors.toList());
String rawLocation = java.net.URLDecoder.decode(meetup.getLocation(), StandardCharsets.UTF_8);
return MeetupMessageModel
.builder()
.description(meetup.getDescription())
.topic(meetup.getTopic())
.location(meetup.getLocation())
.decodedLocation(rawLocation)
.noTimeId(meetup.getNoTimeButtonId())
.yesId(meetup.getYesButtonId())
.maybeId(meetup.getMaybeButtonId())
.noId(meetup.getNotInterestedButtonId())
.meetupTime(meetup.getMeetupTime())
.meetupId(meetup.getId().getId())
.participants(getMemberDisplays(participating))
@@ -153,6 +195,7 @@ public class MeetupServiceBean {
.maybeParticipants(getMemberDisplays(maybe))
.cancelled(meetup.getState().equals(MeetupState.CANCELLED))
.organizer(MemberDisplay.fromAUserInAServer(meetup.getOrganizer()))
.meetupIcsModel(getMeetupICSModel(meetup))
.build();
}
@@ -250,8 +293,8 @@ public class MeetupServiceBean {
}
}
public CompletableFuture<Void> notifyMeetupParticipants(Meetup meetup, String message) {
List<MeetupDecision> decisionsToBeNotified = Arrays.asList(MeetupDecision.MAYBE, MeetupDecision.YES);
public CompletableFuture<Void> notifyMeetupParticipants(Meetup meetup, String message, MeetupDecision toNotify) {
List<MeetupDecision> decisionsToBeNotified = toNotify == null ? Arrays.asList(MeetupDecision.MAYBE, MeetupDecision.YES) : Arrays.asList(toNotify);
List<MemberDisplay> participants = meetup
.getParticipants()
.stream()
@@ -262,6 +305,9 @@ public class MeetupServiceBean {
MeetupNotificationModel model = MeetupNotificationModel
.builder()
.notificationMessage(message)
.meetupId(meetup.getId().getId())
.meetupMessageId(meetup.getMessageId())
.meetupTopic(meetup.getTopic())
.participants(participants)
.build();
MessageChannel channel = channelService.getMessageChannelFromServer(meetup.getServer().getId(), meetup.getMeetupChannel().getId());
@@ -379,8 +425,43 @@ public class MeetupServiceBean {
MessageToSend updatedMeetupMessage = getMeetupMessage(meetupMessageModel);
GuildMessageChannel meetupChannel = channelService.getMessageChannelFromServer(serverId, meetup.getMeetupChannel().getId());
return channelService.editEmbedMessageInAChannel(updatedMeetupMessage.getEmbeds().get(0), meetupChannel, meetup.getMessageId())
return channelService.editMessageInAChannelFuture(updatedMeetupMessage, meetupChannel, meetup.getMessageId())
.thenAccept(message -> log.info("Updated message of meetup {} in channel {} in server {}.", meetupId, meetup.getMeetupChannel().getId(), serverId))
.thenAccept(unused -> fileService.safeDeleteIgnoreException(updatedMeetupMessage.getAttachedFiles().get(0).getFile()))
.exceptionally(throwable -> {
log.info("Failed to update message of meetup {} in channel {} in server {}.", meetupId, meetup.getMeetupChannel().getId(), serverId, throwable);
return null;
});
}
public CompletableFuture<Void> changeMeetupDescription(Meetup meetup, String newDescription) {
meetup.setDescription(newDescription);
return updateMeetupMessage(meetup);
}
public CompletableFuture<Void> changeMeetupLocation(Meetup meetup, String newLocation) {
try {
meetup.setLocation(URLEncoder.encode(newLocation, StandardCharsets.UTF_8.toString()));
} catch (UnsupportedEncodingException e) {
throw new AbstractoRunTimeException(e);
}
return updateMeetupMessage(meetup);
}
public CompletableFuture<Void> changeMeetupTopic(Meetup meetup, String newTopic) {
meetup.setTopic(newTopic);
return updateMeetupMessage(meetup);
}
private CompletableFuture<Void> updateMeetupMessage(Meetup meetup) {
Long meetupId = meetup.getId().getId();
Long serverId = meetup.getId().getServerId();
MeetupMessageModel meetupMessageModel = getMeetupMessageModel(meetup);
MessageToSend updatedMeetupMessage = getMeetupMessage(meetupMessageModel);
GuildMessageChannel meetupChannel = channelService.getMessageChannelFromServer(serverId, meetup.getMeetupChannel().getId());
return channelService.editMessageInAChannelFuture(updatedMeetupMessage, meetupChannel, meetup.getMessageId())
.thenAccept(message -> log.info("Updated message of meetup {} in channel {} in server {}.", meetupId, meetup.getMeetupChannel().getId(), serverId))
.thenAccept(unused -> fileService.safeDeleteIgnoreException(updatedMeetupMessage.getAttachedFiles().get(0).getFile()))
.exceptionally(throwable -> {
log.info("Failed to update message of meetup {} in channel {} in server {}.", meetupId, meetup.getMeetupChannel().getId(), serverId, throwable);
return null;

View File

@@ -1,5 +1,6 @@
package dev.sheldan.sissi.module.meetup.service.management;
import dev.sheldan.abstracto.core.exception.AbstractoRunTimeException;
import dev.sheldan.abstracto.core.models.ServerSpecificId;
import dev.sheldan.abstracto.core.models.database.AChannel;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
@@ -12,6 +13,9 @@ import dev.sheldan.sissi.module.meetup.repository.MeetupRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.time.Instant;
import java.util.List;
@@ -26,22 +30,28 @@ public class MeetupManagementServiceBean {
public static final String MEETUP_COUNTER_KEY = "meetup";
public Meetup createMeetup(Instant timeStamp, String topic, String description, AUserInAServer organizer, AChannel meetupChannel) {
public Meetup createMeetup(Instant timeStamp, String topic, String description, AUserInAServer organizer, AChannel meetupChannel, String location) {
if(timeStamp.isBefore(Instant.now())) {
throw new MeetupPastTimeException();
}
Long meetupId = counterService.getNextCounterValue(organizer.getServerReference(), MEETUP_COUNTER_KEY);
Meetup meetup = Meetup
.builder()
.meetupTime(timeStamp)
.description(description)
.topic(topic)
.organizer(organizer)
.server(organizer.getServerReference())
.meetupChannel(meetupChannel)
.state(MeetupState.NEW)
.id(new ServerSpecificId(organizer.getServerReference().getId(), meetupId))
.build();
Meetup meetup = null;
try {
meetup = Meetup
.builder()
.meetupTime(timeStamp)
.description(description)
.topic(topic)
.location(URLEncoder.encode(location, StandardCharsets.UTF_8.toString()))
.organizer(organizer)
.server(organizer.getServerReference())
.meetupChannel(meetupChannel)
.state(MeetupState.NEW)
.id(new ServerSpecificId(organizer.getServerReference().getId(), meetupId))
.build();
} catch (UnsupportedEncodingException e) {
throw new AbstractoRunTimeException(e);
}
return meetupRepository.save(meetup);
}
@@ -62,12 +72,8 @@ public class MeetupManagementServiceBean {
return meetupRepository.findByState(MeetupState.CANCELLED);
}
public List<Meetup> getFutureMeetups() {
return meetupRepository.findByMeetupTimeGreaterThan(Instant.now());
}
public List<Meetup> getIncomingMeetups() {
return meetupRepository.findByMeetupTimeGreaterThanAndState(Instant.now(), MeetupState.CONFIRMED);
public List<Meetup> getIncomingMeetups(Long serverId, Long channelId) {
return meetupRepository.findByMeetupTimeGreaterThanAndStateAndServer_IdAndMeetupChannel_Id(Instant.now(), MeetupState.CONFIRMED, serverId, channelId);
}
public void deleteMeetups(List<Meetup> meetups) {

View File

@@ -5,4 +5,8 @@ abstracto.systemConfigs.meetupEarlyReminderSeconds.name=meetupEarlyReminderSecon
abstracto.systemConfigs.meetupEarlyReminderSeconds.longValue=604800
abstracto.systemConfigs.meetupLateReminderSeconds.name=meetupLateReminderSeconds
abstracto.systemConfigs.meetupLateReminderSeconds.longValue=86400
abstracto.systemConfigs.meetupLateReminderSeconds.longValue=86400
abstracto.featureModes.attachIcsFile.featureName=meetup
abstracto.featureModes.attachIcsFile.mode=attachIcsFile
abstracto.featureModes.attachIcsFile.enabled=false

View File

@@ -0,0 +1,11 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog dbchangelog.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext dbchangelog.xsd
http://www.liquibase.org/xml/ns/pro dbchangelog.xsd" >
<include file="tables/tables.xml" relativeToChangelogFile="true"/>
<include file="seedData/data.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>

View File

@@ -0,0 +1,20 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog dbchangelog.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext dbchangelog.xsd
http://www.liquibase.org/xml/ns/pro dbchangelog.xsd" >
<property name="utilityModule" value="(SELECT id FROM module WHERE name = 'utility')"/>
<property name="meetupFeature" value="(SELECT id FROM feature WHERE key = 'meetup')"/>
<changeSet author="Sheldan" id="meetup-change_command">
<insert tableName="command">
<column name="name" value="changeMeetup"/>
<column name="module_id" valueComputed="${utilityModule}"/>
<column name="feature_id" valueComputed="${meetupFeature}"/>
</insert>
</changeSet>
</databaseChangeLog>

View File

@@ -0,0 +1,10 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog dbchangelog.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext dbchangelog.xsd
http://www.liquibase.org/xml/ns/pro dbchangelog.xsd" >
<include file="command.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>

View File

@@ -0,0 +1,27 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog dbchangelog.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext dbchangelog.xsd
http://www.liquibase.org/xml/ns/pro dbchangelog.xsd" >
<changeSet author="Sheldan" id="meetup-add_info_columns">
<addColumn tableName="meetup">
<column name="location" type="VARCHAR(100)" />
</addColumn>
<addColumn tableName="meetup">
<column name="yes_button_id" type="VARCHAR(100)" />
</addColumn>
<addColumn tableName="meetup">
<column name="maybe_button_id" type="VARCHAR(100)" />
</addColumn>
<addColumn tableName="meetup">
<column name="no_time_button_id" type="VARCHAR(100)" />
</addColumn>
<addColumn tableName="meetup">
<column name="not_interested_button_id" type="VARCHAR(100)" />
</addColumn>
</changeSet>
</databaseChangeLog>

View File

@@ -0,0 +1,10 @@
<?xml version="1.1" encoding="UTF-8" standalone="no"?>
<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog"
xmlns:ext="http://www.liquibase.org/xml/ns/dbchangelog-ext"
xmlns:pro="http://www.liquibase.org/xml/ns/pro"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.liquibase.org/xml/ns/dbchangelog dbchangelog.xsd
http://www.liquibase.org/xml/ns/dbchangelog-ext dbchangelog.xsd
http://www.liquibase.org/xml/ns/pro dbchangelog.xsd" >
<include file="meetup.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>

View File

@@ -8,4 +8,5 @@
http://www.liquibase.org/xml/ns/pro dbchangelog.xsd" >
<include file="1.1.0/collection.xml" relativeToChangelogFile="true"/>
<include file="1.2.0/collection.xml" relativeToChangelogFile="true"/>
<include file="1.3.18/collection.xml" relativeToChangelogFile="true"/>
</databaseChangeLog>

View File

@@ -3,7 +3,7 @@
<parent>
<groupId>dev.sheldan.sissi.application</groupId>
<artifactId>application</artifactId>
<version>1.3.14</version>
<version>1.4.24-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
@@ -14,6 +14,7 @@
<module>quotes</module>
<module>meetup</module>
<module>debra</module>
<module>rss-news</module>
</modules>

View File

@@ -3,7 +3,7 @@
<parent>
<groupId>dev.sheldan.sissi.application</groupId>
<artifactId>sissi-modules</artifactId>
<version>1.3.14</version>
<version>1.4.24-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>

View File

@@ -21,6 +21,7 @@ import dev.sheldan.sissi.module.quotes.config.QuotesModuleDefinition;
import dev.sheldan.sissi.module.quotes.exception.QuoteNotFoundException;
import dev.sheldan.sissi.module.quotes.model.database.Quote;
import dev.sheldan.sissi.module.quotes.service.QuoteServiceBean;
import net.dv8tion.jda.api.entities.Member;
import net.dv8tion.jda.api.entities.channel.middleman.MessageChannel;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import net.dv8tion.jda.api.interactions.InteractionHook;
@@ -28,7 +29,7 @@ import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.util.Collections;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
@@ -38,6 +39,7 @@ public class QuoteSearch extends AbstractConditionableCommand {
private static final String QUOTE_SEARCH_COMMAND = "quoteSearch";
private static final String QUERY_PARAMETER = "query";
private static final String MEMBER_PARAMETER = "member";
@Autowired
private QuoteServiceBean quoteServiceBean;
@@ -80,7 +82,14 @@ public class QuoteSearch extends AbstractConditionableCommand {
String query = slashCommandParameterService.getCommandOption(QUERY_PARAMETER, event, String.class);
AServer server = serverManagementService.loadServer(event.getGuild().getIdLong());
Optional<Quote> possibleQuote = quoteServiceBean.searchQuote(query, server);
Optional<Quote> possibleQuote;
if(slashCommandParameterService.hasCommandOption(MEMBER_PARAMETER, event)) {
Member targetMember = slashCommandParameterService.getCommandOption(MEMBER_PARAMETER, event, Member.class);
possibleQuote = quoteServiceBean.searchQuote(query, server, targetMember);
} else {
possibleQuote = quoteServiceBean.searchQuote(query, server);
}
Quote quoteToDisplay = possibleQuote.orElseThrow(QuoteNotFoundException::new);
return quoteServiceBean.renderQuoteToMessageToSend(quoteToDisplay)
.thenCompose(messageToSend -> self.replyMessage(event, messageToSend))
@@ -100,7 +109,17 @@ public class QuoteSearch extends AbstractConditionableCommand {
.name(QUERY_PARAMETER)
.type(String.class)
.build();
List<Parameter> parameters = Collections.singletonList(searchParameter);
Parameter memberParameter = Parameter
.builder()
.templated(true)
.name(MEMBER_PARAMETER)
.slashCommandOnly(true)
.optional(true)
.type(Member.class)
.build();
List<Parameter> parameters = Arrays.asList(searchParameter, memberParameter);
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)

View File

@@ -6,7 +6,7 @@ import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.models.database.AUserInAServer;
import lombok.*;
import javax.persistence.*;
import jakarta.persistence.*;
import java.time.Instant;
import java.util.ArrayList;
import java.util.List;

View File

@@ -2,7 +2,7 @@ package dev.sheldan.sissi.module.quotes.model.database;
import lombok.*;
import javax.persistence.*;
import jakarta.persistence.*;
@Builder
@Entity

View File

@@ -12,6 +12,7 @@ import java.util.List;
@Repository
public interface QuoteRepository extends JpaRepository<Quote, ServerSpecificId> {
List<Quote> findByTextContainingAndServer(String text, AServer server);
List<Quote> findByTextContainingAndServerAndAuthor(String text, AServer server, AUserInAServer author);
List<Quote> findByServer(AServer server);
List<Quote> findByAuthor(AUserInAServer author);
Long countByAuthor(AUserInAServer author);

View File

@@ -214,9 +214,27 @@ public class QuoteServiceBean {
return Optional.empty();
}
if(foundQuotes.size() > 1) {
log.info("Found multiple quotes in server {}, returning first one.", server.getId());
log.info("Found multiple quotes in server {}, returning random one.", server.getId());
int randomIndex = secureRandom.nextInt(foundQuotes.size());
return Optional.of(foundQuotes.get(randomIndex));
} else {
return Optional.of(foundQuotes.get(0));
}
}
public Optional<Quote> searchQuote(String query, AServer server, Member targetMember) {
AUserInAServer author = userInServerManagementService.loadOrCreateUser(targetMember);
List<Quote> foundQuotes = quoteRepository.findByTextContainingAndServerAndAuthor(query, server, author);
if(foundQuotes.isEmpty()) {
return Optional.empty();
}
if(foundQuotes.size() > 1) {
log.info("Found multiple quotes in server {}, returning random one.", server.getId());
int randomIndex = secureRandom.nextInt(foundQuotes.size());
return Optional.of(foundQuotes.get(randomIndex));
} else {
return Optional.of(foundQuotes.get(0));
}
return Optional.of(foundQuotes.get(0));
}
public QuoteStatsModel getQuoteStats(Member member) {

View File

@@ -0,0 +1,59 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>sissi-modules</artifactId>
<groupId>dev.sheldan.sissi.application</groupId>
<version>1.4.24-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<groupId>dev.sheldan.sissi.application.module</groupId>
<artifactId>rss-news</artifactId>
<build>
<plugins>
<plugin>
<artifactId>maven-assembly-plugin</artifactId>
<configuration>
<descriptors>
<descriptor>src/main/assembly/liquibase.xml</descriptor>
</descriptors>
</configuration>
<executions>
<execution>
<id>make-assembly</id>
<phase>package</phase>
<goals>
<goal>single</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencies>
<dependency>
<groupId>dev.sheldan.abstracto.scheduling</groupId>
<artifactId>scheduling-int</artifactId>
<version>${abstracto.version}</version>
</dependency>
<dependency>
<groupId>org.apache.jena</groupId>
<artifactId>jena-arq</artifactId>
<version>${apache-jena.version}</version>
</dependency>
<dependency>
<groupId>org.apache.jena</groupId>
<artifactId>jena-core</artifactId>
<version>${apache-jena.version}</version>
</dependency>
<dependency>
<groupId>com.apptasticsoftware</groupId>
<artifactId>rssreader</artifactId>
</dependency>
</dependencies>
</project>

View File

@@ -1,19 +1,18 @@
<assembly xmlns="http://maven.apache.org/ASSEMBLY/2.1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/ASSEMBLY/2.1.0 http://maven.apache.org/xsd/assembly-2.1.0.xsd">
<id>docker-compose</id>
<id>liquibase</id>
<formats>
<format>zip</format>
</formats>
<includeBaseDirectory>false</includeBaseDirectory>
<fileSets>
<fileSet>
<outputDirectory>./docker-compose</outputDirectory>
<directory>${project.basedir}/src/main/resources/</directory>
<outputDirectory>.</outputDirectory>
<directory>${project.basedir}/src/main/resources/migrations</directory>
<includes>
<include>**/*</include>
</includes>
<filtered>true</filtered>
</fileSet>
</fileSets>
</assembly>

View File

@@ -0,0 +1,95 @@
package dev.sheldan.sissi.module.rssnews.commands;
import dev.sheldan.abstracto.core.command.UtilityModuleDefinition;
import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand;
import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
import dev.sheldan.abstracto.core.command.config.HelpInfo;
import dev.sheldan.abstracto.core.command.config.Parameter;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.interaction.InteractionService;
import dev.sheldan.abstracto.core.interaction.slash.SlashCommandConfig;
import dev.sheldan.abstracto.core.interaction.slash.parameter.SlashCommandParameterService;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.service.management.ServerManagementService;
import dev.sheldan.sissi.module.rssnews.config.RssNewsFeatureDefinition;
import dev.sheldan.sissi.module.rssnews.config.RssNewsSlashCommandNames;
import dev.sheldan.sissi.module.rssnews.service.NewsCategoryServiceBean;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@Component
public class CreateNewsCategory extends AbstractConditionableCommand {
private static final String CATEGORY_KEY_NAME_PARAMETER = "categoryName";
private static final String CREATE_SERVER_NEWS_CATEGORY_RESPONSE_TEMPLATE_KEY = "createNewsCategory_response";
@Autowired
private SlashCommandParameterService slashCommandParameterService;
@Autowired
private InteractionService interactionService;
@Autowired
private NewsCategoryServiceBean newsCategoryServiceBean;
@Autowired
private ServerManagementService serverManagementService;
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
String categoryName = slashCommandParameterService.getCommandOption(CATEGORY_KEY_NAME_PARAMETER, event, String.class);
AServer server = serverManagementService.loadOrCreate(event.getGuild().getIdLong());
newsCategoryServiceBean.createCategory(categoryName, server);
return interactionService.replyEmbed(CREATE_SERVER_NEWS_CATEGORY_RESPONSE_TEMPLATE_KEY, event)
.thenApply(interactionHook -> CommandResult.fromSuccess());
}
@Override
public CommandConfiguration getConfiguration() {
Parameter nameParameter = Parameter
.builder()
.templated(true)
.name(CATEGORY_KEY_NAME_PARAMETER)
.type(String.class)
.build();
List<Parameter> parameters = Arrays.asList(nameParameter);
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.build();
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(RssNewsSlashCommandNames.RSS_NEWS)
.groupName("category")
.commandName("create")
.build();
return CommandConfiguration.builder()
.name("createNewsCategory")
.module(UtilityModuleDefinition.UTILITY)
.templated(true)
.slashCommandConfig(slashCommandConfig)
.async(true)
.slashCommandOnly(true)
.supportsEmbedException(true)
.causesReaction(false)
.parameters(parameters)
.help(helpInfo)
.build();
}
@Override
public FeatureDefinition getFeature() {
return RssNewsFeatureDefinition.RSS_NEWS;
}
}

View File

@@ -0,0 +1,122 @@
package dev.sheldan.sissi.module.rssnews.commands;
import dev.sheldan.abstracto.core.command.UtilityModuleDefinition;
import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand;
import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
import dev.sheldan.abstracto.core.command.config.HelpInfo;
import dev.sheldan.abstracto.core.command.config.Parameter;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.interaction.InteractionService;
import dev.sheldan.abstracto.core.interaction.slash.SlashCommandConfig;
import dev.sheldan.abstracto.core.interaction.slash.parameter.SlashCommandAutoCompleteService;
import dev.sheldan.abstracto.core.interaction.slash.parameter.SlashCommandParameterService;
import dev.sheldan.sissi.module.rssnews.config.RssNewsFeatureDefinition;
import dev.sheldan.sissi.module.rssnews.config.RssNewsSlashCommandNames;
import dev.sheldan.sissi.module.rssnews.service.NewsCategoryChannelMappingServiceBean;
import dev.sheldan.sissi.module.rssnews.service.NewsCategoryServiceBean;
import net.dv8tion.jda.api.entities.channel.concrete.TextChannel;
import net.dv8tion.jda.api.entities.channel.middleman.GuildChannel;
import net.dv8tion.jda.api.events.interaction.command.CommandAutoCompleteInteractionEvent;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@Component
public class CreateNewsCategoryChannelMapping extends AbstractConditionableCommand {
private static final String CATEGORY_KEY_NAME_PARAMETER = "categoryName";
private static final String CHANNEL_PARAMETER = "channel";
private static final String CREATE_NEWS_CATEGORY_CHANNEL_MAPPING_RESPONSE_TEMPLATE_KEY = "createNewsCategoryChannelMapping_response";
@Autowired
private SlashCommandParameterService slashCommandParameterService;
@Autowired
private InteractionService interactionService;
@Autowired
private NewsCategoryServiceBean newsCategoryServiceBean;
@Autowired
private SlashCommandAutoCompleteService slashCommandAutoCompleteService;
@Autowired
private NewsCategoryChannelMappingServiceBean newsCategoryMappingServiceBean;
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
String categoryName = slashCommandParameterService.getCommandOption(CATEGORY_KEY_NAME_PARAMETER, event, String.class);
GuildChannel channel = slashCommandParameterService.getCommandOption(CHANNEL_PARAMETER, event, TextChannel.class, GuildChannel.class);
newsCategoryMappingServiceBean.createNewsCategoryChannelMapping(categoryName, channel);
return interactionService.replyEmbed(CREATE_NEWS_CATEGORY_CHANNEL_MAPPING_RESPONSE_TEMPLATE_KEY, event)
.thenApply(interactionHook -> CommandResult.fromSuccess());
}
@Override
public List<String> performAutoComplete(CommandAutoCompleteInteractionEvent event) {
if(slashCommandAutoCompleteService.matchesParameter(event.getFocusedOption(), CATEGORY_KEY_NAME_PARAMETER)) {
return newsCategoryServiceBean.getNamesOfNewsCategoriesStartingWith(event.getFocusedOption().getValue(), event.getGuild());
} else {
return new ArrayList<>();
}
}
@Override
public CommandConfiguration getConfiguration() {
Parameter categoryParameter = Parameter
.builder()
.templated(true)
.name(CATEGORY_KEY_NAME_PARAMETER)
.supportsAutoComplete(true)
.type(String.class)
.build();
Parameter channelParameter = Parameter
.builder()
.name(CHANNEL_PARAMETER)
.type(TextChannel.class)
.templated(true)
.build();
List<Parameter> parameters = Arrays.asList(categoryParameter, channelParameter);
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.build();
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(RssNewsSlashCommandNames.RSS_NEWS)
.groupName("categorychannelmapping")
.commandName("create")
.build();
return CommandConfiguration.builder()
.name("createNewsCategoryChannelMapping")
.module(UtilityModuleDefinition.UTILITY)
.templated(true)
.slashCommandConfig(slashCommandConfig)
.async(true)
.slashCommandOnly(true)
.supportsEmbedException(true)
.parameters(parameters)
.causesReaction(false)
.help(helpInfo)
.build();
}
@Override
public FeatureDefinition getFeature() {
return RssNewsFeatureDefinition.RSS_NEWS;
}
}

View File

@@ -0,0 +1,152 @@
package dev.sheldan.sissi.module.rssnews.commands;
import dev.sheldan.abstracto.core.command.UtilityModuleDefinition;
import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand;
import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
import dev.sheldan.abstracto.core.command.config.HelpInfo;
import dev.sheldan.abstracto.core.command.config.Parameter;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.interaction.InteractionService;
import dev.sheldan.abstracto.core.interaction.slash.SlashCommandConfig;
import dev.sheldan.abstracto.core.interaction.slash.parameter.SlashCommandAutoCompleteService;
import dev.sheldan.abstracto.core.interaction.slash.parameter.SlashCommandParameterService;
import dev.sheldan.sissi.module.rssnews.config.RssNewsFeatureDefinition;
import dev.sheldan.sissi.module.rssnews.config.RssNewsSlashCommandNames;
import dev.sheldan.sissi.module.rssnews.model.database.NewsFeedSource;
import dev.sheldan.sissi.module.rssnews.service.NewsCategoryServiceBean;
import dev.sheldan.sissi.module.rssnews.service.NewsFeedSourceCategoryServiceBean;
import dev.sheldan.sissi.module.rssnews.service.NewsFeedSourceCategorySubscriptionServiceBean;
import dev.sheldan.sissi.module.rssnews.service.management.NewsFeedSourceManagementServiceBean;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.events.interaction.command.CommandAutoCompleteInteractionEvent;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
@Component
public class CreateNewsCategorySubscription extends AbstractConditionableCommand {
private static final String CATEGORY_KEY_NAME_PARAMETER = "categoryName";
private static final String SOURCE_CATEGORY_PARAMETER = "sourceCategory";
private static final String NEWS_FEED_SOURCE_KEY_PARAMETER = "newsFeed";
private static final String CREATE_NEWS_CATEGORY_SUBSCRIPTION_RESPONSE_TEMPLATE_KEY = "createNewsCategorySubscription_response";
@Autowired
private SlashCommandParameterService slashCommandParameterService;
@Autowired
private InteractionService interactionService;
@Autowired
private NewsCategoryServiceBean newsCategoryServiceBean;
@Autowired
private SlashCommandAutoCompleteService slashCommandAutoCompleteService;
@Autowired
private NewsFeedSourceCategorySubscriptionServiceBean newsFeedSourceCategorySubscriptionServiceBean;
@Autowired
private NewsFeedSourceCategoryServiceBean newsFeedSourceCategoryServiceBean;
@Autowired
private NewsFeedSourceManagementServiceBean newsFeedSourceManagementServiceBean;
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
String categoryName = slashCommandParameterService.getCommandOption(CATEGORY_KEY_NAME_PARAMETER, event, String.class);
String sourceCategoryName = slashCommandParameterService.getCommandOption(SOURCE_CATEGORY_PARAMETER, event, String.class);
String newsFeedSourceName = slashCommandParameterService.getCommandOption(NEWS_FEED_SOURCE_KEY_PARAMETER, event, String.class);
Guild guild = event.getGuild();
newsFeedSourceCategorySubscriptionServiceBean.createNewsFeedSourceCategorySubscription(categoryName, sourceCategoryName, newsFeedSourceName, guild);
return interactionService.replyEmbed(CREATE_NEWS_CATEGORY_SUBSCRIPTION_RESPONSE_TEMPLATE_KEY, event)
.thenApply(interactionHook -> CommandResult.fromSuccess());
}
@Override
public List<String> performAutoComplete(CommandAutoCompleteInteractionEvent event) {
if(slashCommandAutoCompleteService.matchesParameter(event.getFocusedOption(), CATEGORY_KEY_NAME_PARAMETER)) {
return newsCategoryServiceBean.getNamesOfNewsCategoriesStartingWith(event.getFocusedOption().getValue(), event.getGuild());
} if(slashCommandAutoCompleteService.matchesParameter(event.getFocusedOption(), SOURCE_CATEGORY_PARAMETER)) {
String newsFeedSourceName = slashCommandParameterService.getCommandOption(NEWS_FEED_SOURCE_KEY_PARAMETER, event, String.class);
Optional<NewsFeedSource> newsFeedSourceOptional = newsFeedSourceManagementServiceBean.getNewsFeedSourceWithNameOptional(newsFeedSourceName);
if(newsFeedSourceOptional.isEmpty()) {
return new ArrayList<>();
}
return newsFeedSourceCategoryServiceBean.getNamesOfNewsSourceCategoriesInNewsFeedStartingWith(event.getFocusedOption().getValue(), newsFeedSourceOptional.get());
} if(slashCommandAutoCompleteService.matchesParameter(event.getFocusedOption(), NEWS_FEED_SOURCE_KEY_PARAMETER)) {
return newsFeedSourceManagementServiceBean.getNewsFeedSourceNamesStartingWithName(event.getFocusedOption().getValue());
} else {
return new ArrayList<>();
}
}
@Override
public CommandConfiguration getConfiguration() {
Parameter categoryParameter = Parameter
.builder()
.templated(true)
.name(CATEGORY_KEY_NAME_PARAMETER)
.supportsAutoComplete(true)
.type(String.class)
.build();
Parameter sourceCategoryParameter = Parameter
.builder()
.name(SOURCE_CATEGORY_PARAMETER)
.type(String.class)
.supportsAutoComplete(true)
.templated(true)
.build();
Parameter newsFeedKeyParameter = Parameter
.builder()
.name(NEWS_FEED_SOURCE_KEY_PARAMETER)
.type(String.class)
.supportsAutoComplete(true)
.templated(true)
.build();
List<Parameter> parameters = Arrays.asList(categoryParameter, newsFeedKeyParameter, sourceCategoryParameter);
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.build();
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(RssNewsSlashCommandNames.RSS_NEWS)
.groupName("categorysubscription")
.commandName("create")
.build();
return CommandConfiguration.builder()
.name("createNewsCategorySubscription")
.module(UtilityModuleDefinition.UTILITY)
.templated(true)
.slashCommandConfig(slashCommandConfig)
.async(true)
.slashCommandOnly(true)
.supportsEmbedException(true)
.parameters(parameters)
.causesReaction(false)
.help(helpInfo)
.build();
}
@Override
public FeatureDefinition getFeature() {
return RssNewsFeatureDefinition.RSS_NEWS;
}
}

View File

@@ -0,0 +1,95 @@
package dev.sheldan.sissi.module.rssnews.commands;
import dev.sheldan.abstracto.core.command.UtilityModuleDefinition;
import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand;
import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
import dev.sheldan.abstracto.core.command.config.HelpInfo;
import dev.sheldan.abstracto.core.command.config.Parameter;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.interaction.InteractionService;
import dev.sheldan.abstracto.core.interaction.slash.SlashCommandConfig;
import dev.sheldan.abstracto.core.interaction.slash.parameter.SlashCommandParameterService;
import dev.sheldan.abstracto.core.models.database.AServer;
import dev.sheldan.abstracto.core.service.management.ServerManagementService;
import dev.sheldan.sissi.module.rssnews.config.RssNewsFeatureDefinition;
import dev.sheldan.sissi.module.rssnews.config.RssNewsSlashCommandNames;
import dev.sheldan.sissi.module.rssnews.service.NewsCategoryServiceBean;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@Component
public class DeleteNewsCategory extends AbstractConditionableCommand {
private static final String CATEGORY_KEY_NAME_PARAMETER = "categoryName";
private static final String DELETE_NEWS_CATEGORY_RESPONSE_TEMPLATE_KEY = "deleteNewsCategory_response";
@Autowired
private SlashCommandParameterService slashCommandParameterService;
@Autowired
private InteractionService interactionService;
@Autowired
private NewsCategoryServiceBean newsCategoryServiceBean;
@Autowired
private ServerManagementService serverManagementService;
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
String categoryName = slashCommandParameterService.getCommandOption(CATEGORY_KEY_NAME_PARAMETER, event, String.class);
AServer server = serverManagementService.loadOrCreate(event.getGuild().getIdLong());
newsCategoryServiceBean.deleteNewsCategoryByName(categoryName, server);
return interactionService.replyEmbed(DELETE_NEWS_CATEGORY_RESPONSE_TEMPLATE_KEY, event)
.thenApply(interactionHook -> CommandResult.fromSuccess());
}
@Override
public CommandConfiguration getConfiguration() {
Parameter nameParameter = Parameter
.builder()
.templated(true)
.name(CATEGORY_KEY_NAME_PARAMETER)
.type(String.class)
.build();
List<Parameter> parameters = Arrays.asList(nameParameter);
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.build();
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(RssNewsSlashCommandNames.RSS_NEWS)
.groupName("category")
.commandName("delete")
.build();
return CommandConfiguration.builder()
.name("deleteNewsCategory")
.module(UtilityModuleDefinition.UTILITY)
.templated(true)
.slashCommandConfig(slashCommandConfig)
.async(true)
.slashCommandOnly(true)
.supportsEmbedException(true)
.causesReaction(false)
.parameters(parameters)
.help(helpInfo)
.build();
}
@Override
public FeatureDefinition getFeature() {
return RssNewsFeatureDefinition.RSS_NEWS;
}
}

View File

@@ -0,0 +1,122 @@
package dev.sheldan.sissi.module.rssnews.commands;
import dev.sheldan.abstracto.core.command.UtilityModuleDefinition;
import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand;
import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
import dev.sheldan.abstracto.core.command.config.HelpInfo;
import dev.sheldan.abstracto.core.command.config.Parameter;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.interaction.InteractionService;
import dev.sheldan.abstracto.core.interaction.slash.SlashCommandConfig;
import dev.sheldan.abstracto.core.interaction.slash.parameter.SlashCommandAutoCompleteService;
import dev.sheldan.abstracto.core.interaction.slash.parameter.SlashCommandParameterService;
import dev.sheldan.sissi.module.rssnews.config.RssNewsFeatureDefinition;
import dev.sheldan.sissi.module.rssnews.config.RssNewsSlashCommandNames;
import dev.sheldan.sissi.module.rssnews.service.NewsCategoryChannelMappingServiceBean;
import dev.sheldan.sissi.module.rssnews.service.NewsCategoryServiceBean;
import net.dv8tion.jda.api.entities.channel.concrete.TextChannel;
import net.dv8tion.jda.api.entities.channel.middleman.GuildChannel;
import net.dv8tion.jda.api.events.interaction.command.CommandAutoCompleteInteractionEvent;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@Component
public class DeleteNewsCategoryChannelMapping extends AbstractConditionableCommand {
private static final String CATEGORY_KEY_NAME_PARAMETER = "categoryName";
private static final String CHANNEL_PARAMETER = "channel";
private static final String DELETE_NEWS_CATEGORY_CHANNEL_MAPPING_RESPONSE_TEMPLATE_KEY = "deleteNewsCategoryChannelMapping_response";
@Autowired
private SlashCommandParameterService slashCommandParameterService;
@Autowired
private InteractionService interactionService;
@Autowired
private NewsCategoryServiceBean newsCategoryServiceBean;
@Autowired
private SlashCommandAutoCompleteService slashCommandAutoCompleteService;
@Autowired
private NewsCategoryChannelMappingServiceBean newsCategoryMappingServiceBean;
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
String categoryName = slashCommandParameterService.getCommandOption(CATEGORY_KEY_NAME_PARAMETER, event, String.class);
GuildChannel channel = slashCommandParameterService.getCommandOption(CHANNEL_PARAMETER, event, TextChannel.class, GuildChannel.class);
newsCategoryMappingServiceBean.deleteNewsCategoryChannelMapping(categoryName, channel);
return interactionService.replyEmbed(DELETE_NEWS_CATEGORY_CHANNEL_MAPPING_RESPONSE_TEMPLATE_KEY, event)
.thenApply(interactionHook -> CommandResult.fromSuccess());
}
@Override
public List<String> performAutoComplete(CommandAutoCompleteInteractionEvent event) {
if(slashCommandAutoCompleteService.matchesParameter(event.getFocusedOption(), CATEGORY_KEY_NAME_PARAMETER)) {
return newsCategoryServiceBean.getNamesOfNewsCategoriesStartingWith(event.getFocusedOption().getValue(), event.getGuild());
} else {
return new ArrayList<>();
}
}
@Override
public CommandConfiguration getConfiguration() {
Parameter categoryParameter = Parameter
.builder()
.templated(true)
.name(CATEGORY_KEY_NAME_PARAMETER)
.supportsAutoComplete(true)
.type(String.class)
.build();
Parameter channelParameter = Parameter
.builder()
.name(CHANNEL_PARAMETER)
.type(TextChannel.class)
.templated(true)
.build();
List<Parameter> parameters = Arrays.asList(categoryParameter, channelParameter);
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.build();
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(RssNewsSlashCommandNames.RSS_NEWS)
.groupName("categorychannelmapping")
.commandName("delete")
.build();
return CommandConfiguration.builder()
.name("deleteNewsCategoryChannelMapping")
.module(UtilityModuleDefinition.UTILITY)
.templated(true)
.slashCommandConfig(slashCommandConfig)
.async(true)
.parameters(parameters)
.slashCommandOnly(true)
.supportsEmbedException(true)
.causesReaction(false)
.help(helpInfo)
.build();
}
@Override
public FeatureDefinition getFeature() {
return RssNewsFeatureDefinition.RSS_NEWS;
}
}

View File

@@ -0,0 +1,152 @@
package dev.sheldan.sissi.module.rssnews.commands;
import dev.sheldan.abstracto.core.command.UtilityModuleDefinition;
import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand;
import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
import dev.sheldan.abstracto.core.command.config.HelpInfo;
import dev.sheldan.abstracto.core.command.config.Parameter;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.interaction.InteractionService;
import dev.sheldan.abstracto.core.interaction.slash.SlashCommandConfig;
import dev.sheldan.abstracto.core.interaction.slash.parameter.SlashCommandAutoCompleteService;
import dev.sheldan.abstracto.core.interaction.slash.parameter.SlashCommandParameterService;
import dev.sheldan.sissi.module.rssnews.config.RssNewsFeatureDefinition;
import dev.sheldan.sissi.module.rssnews.config.RssNewsSlashCommandNames;
import dev.sheldan.sissi.module.rssnews.model.database.NewsFeedSource;
import dev.sheldan.sissi.module.rssnews.service.NewsCategoryServiceBean;
import dev.sheldan.sissi.module.rssnews.service.NewsFeedSourceCategoryServiceBean;
import dev.sheldan.sissi.module.rssnews.service.NewsFeedSourceCategorySubscriptionServiceBean;
import dev.sheldan.sissi.module.rssnews.service.management.NewsFeedSourceManagementServiceBean;
import net.dv8tion.jda.api.entities.Guild;
import net.dv8tion.jda.api.events.interaction.command.CommandAutoCompleteInteractionEvent;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
@Component
public class DeleteNewsCategorySubscription extends AbstractConditionableCommand {
private static final String CATEGORY_KEY_NAME_PARAMETER = "categoryName";
private static final String SOURCE_CATEGORY_PARAMETER = "sourceCategory";
private static final String NEWS_FEED_SOURCE_KEY_PARAMETER = "newsFeed";
private static final String CREATE_NEWS_CATEGORY_SUBSCRIPTION_RESPONSE_TEMPLATE_KEY = "deleteNewsCategorySubscription_response";
@Autowired
private SlashCommandParameterService slashCommandParameterService;
@Autowired
private InteractionService interactionService;
@Autowired
private NewsCategoryServiceBean newsCategoryServiceBean;
@Autowired
private SlashCommandAutoCompleteService slashCommandAutoCompleteService;
@Autowired
private NewsFeedSourceCategorySubscriptionServiceBean newsFeedSourceCategorySubscriptionServiceBean;
@Autowired
private NewsFeedSourceCategoryServiceBean newsFeedSourceCategoryServiceBean;
@Autowired
private NewsFeedSourceManagementServiceBean newsFeedSourceManagementServiceBean;
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
String categoryName = slashCommandParameterService.getCommandOption(CATEGORY_KEY_NAME_PARAMETER, event, String.class);
String sourceCategoryName = slashCommandParameterService.getCommandOption(SOURCE_CATEGORY_PARAMETER, event, String.class);
String newsFeedSourceName = slashCommandParameterService.getCommandOption(NEWS_FEED_SOURCE_KEY_PARAMETER, event, String.class);
Guild guild = event.getGuild();
newsFeedSourceCategorySubscriptionServiceBean.deleteNewsFeedSourceCategorySubscription(categoryName, sourceCategoryName, newsFeedSourceName, guild);
return interactionService.replyEmbed(CREATE_NEWS_CATEGORY_SUBSCRIPTION_RESPONSE_TEMPLATE_KEY, event)
.thenApply(interactionHook -> CommandResult.fromSuccess());
}
@Override
public List<String> performAutoComplete(CommandAutoCompleteInteractionEvent event) {
if(slashCommandAutoCompleteService.matchesParameter(event.getFocusedOption(), CATEGORY_KEY_NAME_PARAMETER)) {
return newsCategoryServiceBean.getNamesOfNewsCategoriesStartingWith(event.getFocusedOption().getValue(), event.getGuild());
} if(slashCommandAutoCompleteService.matchesParameter(event.getFocusedOption(), SOURCE_CATEGORY_PARAMETER)) {
String newsFeedSourceName = slashCommandParameterService.getCommandOption(NEWS_FEED_SOURCE_KEY_PARAMETER, event, String.class);
Optional<NewsFeedSource> newsFeedSourceOptional = newsFeedSourceManagementServiceBean.getNewsFeedSourceWithNameOptional(newsFeedSourceName);
if(newsFeedSourceOptional.isEmpty()) {
return new ArrayList<>();
}
return newsFeedSourceCategoryServiceBean.getNamesOfNewsSourceCategoriesInNewsFeedStartingWith(event.getFocusedOption().getValue(), newsFeedSourceOptional.get());
} if(slashCommandAutoCompleteService.matchesParameter(event.getFocusedOption(), NEWS_FEED_SOURCE_KEY_PARAMETER)) {
return newsFeedSourceManagementServiceBean.getNewsFeedSourceNamesStartingWithName(event.getFocusedOption().getValue());
} else {
return new ArrayList<>();
}
}
@Override
public CommandConfiguration getConfiguration() {
Parameter categoryParameter = Parameter
.builder()
.templated(true)
.name(CATEGORY_KEY_NAME_PARAMETER)
.supportsAutoComplete(true)
.type(String.class)
.build();
Parameter sourceCategoryParameter = Parameter
.builder()
.name(SOURCE_CATEGORY_PARAMETER)
.type(String.class)
.supportsAutoComplete(true)
.templated(true)
.build();
Parameter newsFeedKeyParameter = Parameter
.builder()
.name(NEWS_FEED_SOURCE_KEY_PARAMETER)
.type(String.class)
.supportsAutoComplete(true)
.templated(true)
.build();
List<Parameter> parameters = Arrays.asList(categoryParameter, newsFeedKeyParameter, sourceCategoryParameter);
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.build();
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(RssNewsSlashCommandNames.RSS_NEWS)
.groupName("categorysubscription")
.commandName("delete")
.build();
return CommandConfiguration.builder()
.name("deleteNewsCategorySubscription")
.module(UtilityModuleDefinition.UTILITY)
.templated(true)
.slashCommandConfig(slashCommandConfig)
.async(true)
.slashCommandOnly(true)
.supportsEmbedException(true)
.parameters(parameters)
.causesReaction(false)
.help(helpInfo)
.build();
}
@Override
public FeatureDefinition getFeature() {
return RssNewsFeatureDefinition.RSS_NEWS;
}
}

View File

@@ -0,0 +1,78 @@
package dev.sheldan.sissi.module.rssnews.commands;
import dev.sheldan.abstracto.core.command.UtilityModuleDefinition;
import dev.sheldan.abstracto.core.command.condition.AbstractConditionableCommand;
import dev.sheldan.abstracto.core.command.config.CommandConfiguration;
import dev.sheldan.abstracto.core.command.config.HelpInfo;
import dev.sheldan.abstracto.core.command.execution.CommandResult;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.abstracto.core.interaction.InteractionService;
import dev.sheldan.abstracto.core.interaction.slash.SlashCommandConfig;
import dev.sheldan.sissi.module.rssnews.config.RssNewsFeatureDefinition;
import dev.sheldan.sissi.module.rssnews.config.RssNewsSlashCommandNames;
import dev.sheldan.sissi.module.rssnews.model.template.NewsCategoryInfo;
import dev.sheldan.sissi.module.rssnews.model.template.ShowNewsCategoriesResponse;
import dev.sheldan.sissi.module.rssnews.service.NewsCategoryServiceBean;
import net.dv8tion.jda.api.events.interaction.command.SlashCommandInteractionEvent;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@Component
public class ShowNewsCategories extends AbstractConditionableCommand {
private static final String SHOW_NEWS_CATEGORIES_TEMPLATE_KEY = "showNewsCategories_response";
@Autowired
private NewsCategoryServiceBean newsCategoryServiceBean;
@Autowired
private InteractionService interactionService;
@Override
public CompletableFuture<CommandResult> executeSlash(SlashCommandInteractionEvent event) {
List<NewsCategoryInfo> categoryInfos = newsCategoryServiceBean.getCategoryInfos(event.getGuild());
ShowNewsCategoriesResponse responseModel = ShowNewsCategoriesResponse
.builder()
.newsCategories(categoryInfos)
.build();
return interactionService.replyEmbed(SHOW_NEWS_CATEGORIES_TEMPLATE_KEY, responseModel, event)
.thenApply(interactionHook -> CommandResult.fromSuccess());
}
@Override
public CommandConfiguration getConfiguration() {
HelpInfo helpInfo = HelpInfo
.builder()
.templated(true)
.build();
SlashCommandConfig slashCommandConfig = SlashCommandConfig
.builder()
.enabled(true)
.rootCommandName(RssNewsSlashCommandNames.RSS_NEWS)
.groupName("category")
.commandName("show")
.build();
return CommandConfiguration.builder()
.name("showNewsCategories")
.module(UtilityModuleDefinition.UTILITY)
.templated(true)
.slashCommandConfig(slashCommandConfig)
.async(true)
.slashCommandOnly(true)
.supportsEmbedException(true)
.causesReaction(false)
.help(helpInfo)
.build();
}
@Override
public FeatureDefinition getFeature() {
return RssNewsFeatureDefinition.RSS_NEWS;
}
}

View File

@@ -0,0 +1,28 @@
package dev.sheldan.sissi.module.rssnews.config;
import dev.sheldan.abstracto.core.config.FeatureConfig;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import dev.sheldan.sissi.module.rssnews.orf.config.OrfNewsFeatureConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.Arrays;
import java.util.List;
@Component
public class RSSNewsFeatureConfig implements FeatureConfig {
@Autowired
private OrfNewsFeatureConfig orfNewsFeatureConfig;
@Override
public FeatureDefinition getFeature() {
return RssNewsFeatureDefinition.RSS_NEWS;
}
@Override
public List<FeatureConfig> getDependantFeatures() {
return Arrays.asList(orfNewsFeatureConfig);
}
}

View File

@@ -0,0 +1,15 @@
package dev.sheldan.sissi.module.rssnews.config;
import dev.sheldan.abstracto.core.config.FeatureDefinition;
import lombok.Getter;
@Getter
public enum RssNewsFeatureDefinition implements FeatureDefinition {
RSS_NEWS("rssNews"), ORF_NEWS("orfNews");
private String key;
RssNewsFeatureDefinition(String key) {
this.key = key;
}
}

View File

@@ -0,0 +1,9 @@
package dev.sheldan.sissi.module.rssnews.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
@Configuration
@PropertySource("classpath:rss-news.properties")
public class RssNewsProperties {
}

View File

@@ -0,0 +1,5 @@
package dev.sheldan.sissi.module.rssnews.config;
public class RssNewsSlashCommandNames {
public static final String RSS_NEWS = "rssnews";
}

View File

@@ -0,0 +1,21 @@
package dev.sheldan.sissi.module.rssnews.exception;
import dev.sheldan.abstracto.core.exception.AbstractoTemplatableException;
public class NewsCategoryAlreadyExistsException extends AbstractoTemplatableException {
public NewsCategoryAlreadyExistsException() {
super("News Category already exists.");
}
@Override
public String getTemplateName() {
return "news_category_already_exists_exception";
}
@Override
public Object getTemplateModel() {
return new Object();
}
}

View File

@@ -0,0 +1,21 @@
package dev.sheldan.sissi.module.rssnews.exception;
import dev.sheldan.abstracto.core.exception.AbstractoTemplatableException;
public class NewsCategoryChannelMappingAlreadyExistsException extends AbstractoTemplatableException {
public NewsCategoryChannelMappingAlreadyExistsException() {
super("News Category channel mapping already exists.");
}
@Override
public String getTemplateName() {
return "news_category_channel_mapping_already_exists_exception";
}
@Override
public Object getTemplateModel() {
return new Object();
}
}

View File

@@ -0,0 +1,20 @@
package dev.sheldan.sissi.module.rssnews.exception;
import dev.sheldan.abstracto.core.exception.AbstractoTemplatableException;
public class NewsCategoryChannelMappingNotFoundException extends AbstractoTemplatableException {
public NewsCategoryChannelMappingNotFoundException() {
super("News Category channel mapping not found.");
}
@Override
public String getTemplateName() {
return "news_category_channel_mapping_not_found_exception";
}
@Override
public Object getTemplateModel() {
return new Object();
}
}

View File

@@ -0,0 +1,20 @@
package dev.sheldan.sissi.module.rssnews.exception;
import dev.sheldan.abstracto.core.exception.AbstractoTemplatableException;
public class NewsCategoryNotFoundException extends AbstractoTemplatableException {
public NewsCategoryNotFoundException() {
super("News Category not found.");
}
@Override
public String getTemplateName() {
return "news_category_not_found_exception";
}
@Override
public Object getTemplateModel() {
return new Object();
}
}

View File

@@ -0,0 +1,21 @@
package dev.sheldan.sissi.module.rssnews.exception;
import dev.sheldan.abstracto.core.exception.AbstractoTemplatableException;
public class NewsFeedSourceCategoryNotFoundException extends AbstractoTemplatableException {
public NewsFeedSourceCategoryNotFoundException() {
super("News feed source category not found.");
}
@Override
public String getTemplateName() {
return "news_feed_source_category_not_found_exception";
}
@Override
public Object getTemplateModel() {
return new Object();
}
}

View File

@@ -0,0 +1,21 @@
package dev.sheldan.sissi.module.rssnews.exception;
import dev.sheldan.abstracto.core.exception.AbstractoTemplatableException;
public class NewsFeedSourceCategorySubscriptionAlreadyExistsException extends AbstractoTemplatableException {
public NewsFeedSourceCategorySubscriptionAlreadyExistsException() {
super("News feed source category subscription already exists.");
}
@Override
public String getTemplateName() {
return "news_feed_source_category_subscription_already_exists_exception";
}
@Override
public Object getTemplateModel() {
return new Object();
}
}

View File

@@ -0,0 +1,21 @@
package dev.sheldan.sissi.module.rssnews.exception;
import dev.sheldan.abstracto.core.exception.AbstractoTemplatableException;
public class NewsFeedSourceCategorySubscriptionNotFoundException extends AbstractoTemplatableException {
public NewsFeedSourceCategorySubscriptionNotFoundException() {
super("News feed source category subscription not found.");
}
@Override
public String getTemplateName() {
return "news_feed_source_category_subscription_not_found_exception";
}
@Override
public Object getTemplateModel() {
return new Object();
}
}

View File

@@ -0,0 +1,21 @@
package dev.sheldan.sissi.module.rssnews.exception;
import dev.sheldan.abstracto.core.exception.AbstractoTemplatableException;
public class NewsFeedSourceNotFoundException extends AbstractoTemplatableException {
public NewsFeedSourceNotFoundException() {
super("News feed source not found.");
}
@Override
public String getTemplateName() {
return "news_feed_source_not_found_exception";
}
@Override
public Object getTemplateModel() {
return new Object();
}
}

Some files were not shown because too many files have changed in this diff Show More