├── .gitattributes
├── .github
└── workflow
│ └── tests.yml
├── .gitignore
├── LICENSE
├── README.md
├── img
├── delphi-notify.png
└── ntfy.svg
├── samples
├── Examples.groupproj
├── ExamplesAndroid.groupproj
├── android
│ ├── AndroidManifest.xml
│ ├── NtfyAndroid.dpr
│ ├── NtfyAndroid.dproj
│ ├── README.md
│ ├── assets
│ │ ├── android-demo.PNG
│ │ ├── android-intent.png
│ │ ├── android-notifi.png
│ │ └── android-sync.png
│ ├── services
│ │ └── ntfy-service
│ │ │ ├── NtfyServiceLocal.dpr
│ │ │ ├── NtfyServiceLocal.dproj
│ │ │ └── src
│ │ │ ├── Service.Local.Ntfy.dfm
│ │ │ └── Service.Local.Ntfy.pas
│ └── src
│ │ ├── Intent.Service.Helper.pas
│ │ ├── View.Main.fmx
│ │ └── View.Main.pas
├── console
│ ├── publisher
│ │ ├── ConsolePublisher.dpr
│ │ ├── ConsolePublisher.dproj
│ │ ├── ConsolePublisher.res
│ │ ├── README.md
│ │ └── src
│ │ │ ├── Example.Action.HTTP.pas
│ │ │ ├── Example.Action.Header.pas
│ │ │ ├── Example.Action.View.pas
│ │ │ ├── Example.Advanced.Configs.pas
│ │ │ ├── Example.Attachments.pas
│ │ │ ├── Example.Basic.pas
│ │ │ ├── Example.Email.pas
│ │ │ ├── Example.Emojis.pas
│ │ │ ├── Example.Icons.pas
│ │ │ ├── Example.Messages.pas
│ │ │ ├── Example.Priorities.pas
│ │ │ ├── Example.Schedule.Delivery.pas
│ │ │ └── Example.URL.Attachments.pas
│ └── subscriber
│ │ ├── ConsoleSubscriber.dpr
│ │ ├── ConsoleSubscriber.dproj
│ │ ├── ConsoleSubscriber.res
│ │ └── README.md
├── openssl
│ ├── README.md
│ ├── lib.zip
│ ├── lib32Bits.zip
│ └── lib64Bits.zip
├── vcl
│ ├── publisher
│ │ ├── Ntfy-54x.ico
│ │ ├── README.md
│ │ ├── VCLPublisher.dpr
│ │ ├── VCLPublisher.dproj
│ │ ├── VCLPublisher.res
│ │ ├── VCLPublisher_Icon.ico
│ │ ├── img
│ │ │ ├── lego-wars.jpg
│ │ │ ├── orion.jpg
│ │ │ └── vcl-sample.PNG
│ │ └── src
│ │ │ ├── Example.Notification.pas
│ │ │ ├── View.Main.dfm
│ │ │ └── View.Main.pas
│ └── subscriber
│ │ ├── Ntfy-54x.ico
│ │ ├── README.md
│ │ ├── VCLSubscriber.Artwork
│ │ └── Windows
│ │ │ ├── AppIcon.icns
│ │ │ ├── AppIcon.ico
│ │ │ ├── Uwp_150.png
│ │ │ └── Uwp_44.png
│ │ ├── VCLSubscriber.dpr
│ │ ├── VCLSubscriber.dproj
│ │ ├── VCLSubscriber.res
│ │ ├── img
│ │ └── subscriber-vcl.png
│ │ └── src
│ │ ├── Example.Push.Notifications.pas
│ │ ├── View.Main.dfm
│ │ └── View.Main.pas
└── win-service
│ └── README.md
├── src
├── NX.Horizon.pas
├── Notify.Action.Contract.pas
├── Notify.Action.DTO.pas
├── Notify.Action.pas
├── Notify.Api.Contract.pas
├── Notify.Api.Factory.pas
├── Notify.Api.Indy.pas
├── Notify.Api.NetHTTP.pas
├── Notify.Api.Response.pas
├── Notify.Attachment.Contract.pas
├── Notify.Attachment.DTO.pas
├── Notify.Attachment.pas
├── Notify.Config.Contract.pas
├── Notify.Config.pas
├── Notify.Core.Contract.pas
├── Notify.Core.pas
├── Notify.Custom.Types.pas
├── Notify.Error.pas
├── Notify.Event.Contract.pas
├── Notify.Event.DTO.pas
├── Notify.Event.pas
├── Notify.Facade.pas
├── Notify.JSON.Parser.pas
├── Notify.Logs.pas
├── Notify.Notification.Contract.pas
├── Notify.Notification.DTO.pas
├── Notify.Notification.pas
├── Notify.Response.Data.pas
├── Notify.SmartPointer.pas
├── Notify.Subscription.Event.pas
├── Notify.Types.pas
└── Notify.pas
└── tests
├── NtfyForDelphiTests.dpr
├── NtfyForDelphiTests.dproj
├── NtfyForDelphiTests.res
├── README.md
├── docker-compose.yml
├── server.yml
└── src
├── Index.pas
├── Test.Action.HTTP.pas
├── Test.Action.Header.pas
├── Test.Action.View.pas
├── Test.Attachments.pas
├── Test.Constants.pas
├── Test.Email.pas
├── Test.Emojis.pas
├── Test.Icons.pas
├── Test.Simple.Message.pas
├── Test.Subscription.pas
└── Test.URL.Attachments.pas
/.gitattributes:
--------------------------------------------------------------------------------
1 | # Auto detect text files and perform LF normalization
2 | * text=auto
3 |
--------------------------------------------------------------------------------
/.github/workflow/tests.yml:
--------------------------------------------------------------------------------
1 | name: Delphi CI/CD
2 |
3 | on:
4 | push:
5 | branches:
6 | - net-http
7 |
8 | jobs:
9 | build:
10 | runs-on: alpine:latest
11 |
12 | steps:
13 | - name: Checkout code
14 | uses: actions/checkout@v2
15 |
16 | - name: Install Wine
17 | run: sudo apt-get update && sudo apt-get install wine64
18 |
19 | - name: Run Delphi Tests
20 | run: |
21 | wine ../../bin/win32/NtfyForDelphiTests.exe
22 | $status=$?
23 | if [ $status -eq 0 ]; then
24 | echo "Tests passed"
25 | else
26 | echo "Tests failed"
27 | fi
28 | exit $status
29 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Uncomment these types if you want even more clean repository. But be careful.
2 | # It can make harm to an existing project source. Read explanations below.
3 | #
4 | # Resource files are binaries containing manifest, project icon and version info.
5 | # They can not be viewed as text or compared by diff-tools. Consider replacing them with .rc files.
6 | #*.res
7 | #
8 | # Type library file (binary). In old Delphi versions it should be stored.
9 | # Since Delphi 2009 it is produced from .ridl file and can safely be ignored.
10 | #*.tlb
11 | #
12 | # Diagram Portfolio file. Used by the diagram editor up to Delphi 7.
13 | # Uncomment this if you are not using diagrams or use newer Delphi version.
14 | #*.ddp
15 | #
16 | # Visual LiveBindings file. Added in Delphi XE2.
17 | # Uncomment this if you are not using LiveBindings Designer.
18 | #*.vlb
19 | #
20 | # Deployment Manager configuration file for your project. Added in Delphi XE2.
21 | # Uncomment this if it is not mobile development and you do not use remote debug feature.
22 | #*.deployproj
23 | #
24 | # C++ object files produced when C/C++ Output file generation is configured.
25 | # Uncomment this if you are not using external objects (zlib library for example).
26 | #*.obj
27 | #
28 |
29 | # Delphi compiler-generated binaries (safe to delete)
30 | *.exe
31 | *.dll
32 | *.bpl
33 | *.bpi
34 | *.dcp
35 | *.so
36 | *.apk
37 | *.drc
38 | *.map
39 | *.dres
40 | *.rsm
41 | *.tds
42 | *.dcu
43 | *.lib
44 | *.a
45 | *.o
46 | *.ocx
47 | *.res
48 | *.rc
49 |
50 |
51 | # Delphi autogenerated files (duplicated info)
52 | *.cfg
53 | *.hpp
54 | *Resource.rc
55 |
56 | # Delphi local files (user-specific info)
57 | *.local
58 | *.identcache
59 | *.projdata
60 | *.tvsconfig
61 | *.dsk
62 |
63 | # Delphi history and backups
64 | __history/
65 | __recovery/
66 | *.~*
67 |
68 | # Castalia statistics file (since XE7 Castalia is distributed with Delphi)
69 | *.stat
70 |
71 | # Boss dependency manager vendor folder https://github.com/HashLoad/boss
72 | modules/
73 |
74 | #Android
75 | *Android64/
76 | *Android32/
77 | *.res
78 | *.xml
79 | *.deployproj
80 | *res/
81 | *bin/
82 | *samples/android/**/*.java
83 | *JavaClasses/
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2022 Samuel
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | ## Ntfy for Delphi
6 |
7 | This library is a client-side implementation for [ntfy](https://ntfy.sh/) server, made in Delphi. You can send and receive instant notifications via http. The maintainer of [ntfy](https://ntfy.sh/) is [Philipp C. Heckel](https://github.com/binwiederhier). As he stated, this service will remain free, and every kind of support to help affording with cloud hosting will be warmly received. You too can self-host a ntfy server. Visit [docs.ntfy.sh](https://docs.ntfy.sh/) to get started and also don't forget to leave a star [on his project](https://github.com/binwiederhier/ntfy).
8 |
9 | ## Manual Installation
10 |
11 | You need to add ```src``` folder to the library path or search path of your project.
12 |
13 | ## Quickstart
14 |
15 | You can push notifications in topics. Topics are like channels and the name you choose will become a public url, so make sure to not choose an easy one to guess.
16 |
17 | ``` pascal
18 |
19 | uses
20 | Notify;
21 |
22 | begin
23 | Ntfy.Notification(
24 | New.Notification
25 | .Topic('your-very-secret-topic')
26 | .Title('⚾ Go to the game')
27 | .MessageContent('Tomorrow at 10:00hs ')
28 | );
29 |
30 | Ntfy.Publish;
31 | end;
32 |
33 | ```
34 |
35 | ## Subscribe to a topic
36 |
37 | You can subscribe by many ways. For instance, the [Web App](https://ntfy.sh/app), [Android](https://docs.ntfy.sh/subscribe/phone/), [CLI](https://docs.ntfy.sh/subscribe/cli/) or you can use this library this way:
38 |
39 | ``` pascal
40 | uses
41 | Notify;
42 |
43 | begin
44 | Ntfy.Subscribe('your-very-secret-topic',
45 | procedure (AEvent: INotifyEvent)
46 | begin
47 | WriteLn('You received a message: ' + AEvent.MessageContent)
48 | end);
49 | end;
50 |
51 | ```
52 |
53 | ## Supported Versions & Platforms
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 | IOS still rely on FCM and "inteligently" decides to kill background/foreground processes when it wants. It was not properly tested yet. All other platforms have been tested and can either publish or maintain a subscription background activity suspended for long hours without having any issues. Refer to these [samples](https://github.com/hazzelnuts/ntfy-for-delphi/tree/main/sample) to learn to use in your project. No tests have been performed on Linux at the present moment.
64 |
65 | ## Built-in dependencies
66 |
67 | Ntfy for Delphi makes use of a few libraries to subscribe and publish. There is no need to install them. Respective credit is awarded to the creators:
68 |
69 | * [NxHorizon](https://github.com/dalijap/nx-horizon) by Dalija Prasnikar.
70 | * [JsonToDelphiClass](https://github.com/PKGeorgiev) by Petar Georgiev.
71 | * [NetHTTP](https://docwiki.embarcadero.com/RADStudio/Rio/en/Using_an_HTTP_Client) native components.
72 | * [Indy10](https://github.com/IndySockets/Indy) by IndySockets.
73 |
74 |
75 | ## Wiki
76 |
77 | Check the [wiki](https://github.com/hazzelnuts/ntfy-for-delphi/wiki) page for specific instructions, updates or tutorials. I've created this implementation for passion and curiosity and it will remain an open source project under the MIT license. Feel free to use, contribute and improve this project!
--------------------------------------------------------------------------------
/img/delphi-notify.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hazzelnuts/ntfy-for-delphi/fd0d51efafd6954bfd1aaaae98fc07531149bb6a/img/delphi-notify.png
--------------------------------------------------------------------------------
/img/ntfy.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/samples/Examples.groupproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | {F264D633-D2C1-4727-905B-2D467A832EC5}
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | Default.Personality.12
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
--------------------------------------------------------------------------------
/samples/ExamplesAndroid.groupproj:
--------------------------------------------------------------------------------
1 |
2 |
3 | {DEF0B80B-12A3-4468-895D-2D1A735A084A}
4 |
5 |
6 |
7 | android\services\ntfy-service\NtfyServiceLocal.dproj
8 |
9 |
10 |
11 |
12 |
13 |
14 | Default.Personality.12
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/samples/android/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
34 |
35 |
36 |
37 |
38 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
55 |
56 |
57 |
59 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
--------------------------------------------------------------------------------
/samples/android/NtfyAndroid.dpr:
--------------------------------------------------------------------------------
1 | program NtfyAndroid;
2 |
3 | uses
4 | System.StartUpCopy,
5 | FMX.Forms,
6 | View.Main in 'src\View.Main.pas' {Form1},
7 | Intent.Service.Helper in 'src\Intent.Service.Helper.pas',
8 | Service.Local.Ntfy in 'services\ntfy-service\src\Service.Local.Ntfy.pas' {DM: TAndroidService},
9 | FMX.Skia {DM: TAndroidService};
10 |
11 | {$R *.res}
12 |
13 | begin
14 | GlobalUseSkia := True;
15 |
16 | Application.Initialize;
17 | Application.CreateForm(TForm1, Form1);
18 | Application.Run;
19 | end.
20 |
--------------------------------------------------------------------------------
/samples/android/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # Android
4 |
5 |
6 |
7 | Compile and explore the Android FMX Demo. Also check this [link](https://github.com/p-samuel/delphi-notify/tree/dev-psamuel/sample/console/publisher) for other resources.
8 |
9 |
15 |
16 | ## Notes
17 |
18 | This demo brings only the essential methods you may need to implement the publisher and subscriber in your project. It was developed on Delphi 12 and comes with Skia enabled.
19 |
--------------------------------------------------------------------------------
/samples/android/assets/android-demo.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hazzelnuts/ntfy-for-delphi/fd0d51efafd6954bfd1aaaae98fc07531149bb6a/samples/android/assets/android-demo.PNG
--------------------------------------------------------------------------------
/samples/android/assets/android-intent.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hazzelnuts/ntfy-for-delphi/fd0d51efafd6954bfd1aaaae98fc07531149bb6a/samples/android/assets/android-intent.png
--------------------------------------------------------------------------------
/samples/android/assets/android-notifi.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hazzelnuts/ntfy-for-delphi/fd0d51efafd6954bfd1aaaae98fc07531149bb6a/samples/android/assets/android-notifi.png
--------------------------------------------------------------------------------
/samples/android/assets/android-sync.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hazzelnuts/ntfy-for-delphi/fd0d51efafd6954bfd1aaaae98fc07531149bb6a/samples/android/assets/android-sync.png
--------------------------------------------------------------------------------
/samples/android/services/ntfy-service/NtfyServiceLocal.dpr:
--------------------------------------------------------------------------------
1 | program NtfyServiceLocal;
2 |
3 | uses
4 | System.Android.ServiceApplication,
5 | Service.Local.Ntfy in 'src\Service.Local.Ntfy.pas' {DM: TAndroidService},
6 | Intent.Service.Helper in '..\..\src\Intent.Service.Helper.pas';
7 |
8 | {$R *.res}
9 |
10 | begin
11 | Application.Initialize;
12 | Application.CreateForm(TDM, DM);
13 | Application.Run;
14 | end.
15 |
--------------------------------------------------------------------------------
/samples/android/services/ntfy-service/src/Service.Local.Ntfy.dfm:
--------------------------------------------------------------------------------
1 | object DM: TDM
2 | OnCreate = AndroidServiceCreate
3 | OnDestroy = AndroidServiceDestroy
4 | OnStartCommand = AndroidServiceStartCommand
5 | Height = 238
6 | Width = 324
7 | object NotificationCenter: TNotificationCenter
8 | Left = 128
9 | Top = 94
10 | end
11 | end
12 |
--------------------------------------------------------------------------------
/samples/android/services/ntfy-service/src/Service.Local.Ntfy.pas:
--------------------------------------------------------------------------------
1 | unit Service.Local.Ntfy;
2 |
3 | interface
4 |
5 | uses
6 | System.Android.Service,
7 | System.Classes,
8 | System.SysUtils,
9 | System.Generics.Collections,
10 | System.Notification,
11 | System.SyncObjs,
12 | System.DateUtils,
13 | Androidapi.JNI.GraphicsContentViewText,
14 | Androidapi.JNI.App,
15 | Androidapi.JNI.Os,
16 | Androidapi.JNI.JavaTypes,
17 | Androidapi.JNI.Support,
18 | Androidapi.JNIBridge,
19 | Androidapi.Helpers,
20 | Notify;
21 |
22 | type
23 |
24 | TDM = class(TAndroidService)
25 | NotificationCenter: TNotificationCenter;
26 | function AndroidServiceStartCommand(const Sender: TObject; const Intent: JIntent; Flags, StartId: Integer): Integer;
27 | procedure AndroidServiceCreate(Sender: TObject);
28 | procedure AndroidServiceDestroy(Sender: TObject);
29 | private
30 | FNotificationManager: JNotificationManager;
31 | FNotificationBuilder: Japp_NotificationCompat_Builder;
32 | FChannelId: String;
33 | FThreads: TObjectList;
34 | FWakeLock: JPowerManager_WakeLock;
35 | procedure ShowUpNotification(const Text: String);
36 | procedure ShowUpNtfy(AEvent: INotifyEvent);
37 | end;
38 |
39 | var
40 | DM: TDM;
41 |
42 | implementation
43 |
44 | uses
45 | Intent.Service.Helper;
46 |
47 | const
48 | FOREGROUND_SERVICE_TYPE_DATA_SYNC = $00000001;
49 |
50 | {%CLASSGROUP 'FMX.Controls.TControl'}
51 |
52 | {$R *.dfm}
53 |
54 | { TDM }
55 |
56 | procedure TDM.AndroidServiceCreate(Sender: TObject);
57 | var
58 | Context: JContext;
59 | LChannel: JNotificationChannel;
60 | LPowerManager: JPowerManager;
61 | begin
62 | try
63 |
64 | //Create threads object
65 | FThreads := TObjectList.Create;
66 |
67 | // Initialize Notification Manager and Builder
68 | FChannelId := 'ntfy-for-delphi';
69 | FNotificationManager := TJNotificationManager.Wrap((TAndroidHelper.Context.getSystemService(TJContext.JavaClass.NOTIFICATION_SERVICE)));
70 | FNotificationBuilder := TJapp_NotificationCompat_Builder.JavaClass.init(TAndroidHelper.Context);
71 | FNotificationBuilder.setSmallIcon(TAndroidHelper.Context.getApplicationInfo.icon);
72 | FNotificationBuilder.setContentTitle(StrToJCharSequence('Ntfy service started'));
73 | FNotificationBuilder.setContentText(StrToJCharSequence('Syncing...'));
74 | FNotificationBuilder.setAutoCancel(True);
75 | FNotificationBuilder.setChannelId(StringToJString(FChannelId));
76 |
77 | //Creates notification channel if not exists
78 | if TJBuild_VERSION.JavaClass.SDK_INT >= 26 then
79 | begin
80 | if FNotificationManager.getNotificationChannel(StringToJString(FChannelId)) = nil then
81 | begin
82 | LChannel := TJNotificationChannel.JavaClass.init(
83 | StringToJString(FChannelId),
84 | StrToJCharSequence('Ntfy Subscription'),
85 | TJNotificationManager.JavaClass.IMPORTANCE_HIGH);
86 | LChannel.setLightColor(TJColor.JavaClass.BLUE);
87 | LChannel.setLockscreenVisibility(TJNotification.JavaClass.VISIBILITY_PRIVATE);
88 | FNotificationManager.createNotificationChannel(LChannel);
89 | end;
90 | end;
91 |
92 | // Creates wake lock object
93 | LPowerManager := TJPowerManager.Wrap(TAndroidHelper.Context.getSystemService(TJContext.JavaClass.POWER_SERVICE));
94 | FWakeLock := LPowerManager.newWakeLock(TJPowerManager.JavaClass.PARTIAL_WAKE_LOCK, StringToJString('MyApp::NtfyWakeLock'));
95 |
96 | except
97 | on E: Exception do
98 | ShowUpNotification('Create: ' + E.Message);
99 | end;
100 |
101 | end;
102 |
103 | procedure TDM.AndroidServiceDestroy(Sender: TObject);
104 | begin
105 | FThreads.Free;
106 | if FWakeLock <> nil then
107 | begin
108 | if FWakeLock.isHeld then
109 | FWakeLock.release();
110 | FWakeLock := nil;
111 | end;
112 | end;
113 |
114 | function TDM.AndroidServiceStartCommand(const Sender: TObject; const Intent: JIntent; Flags, StartId: Integer): Integer;
115 | var
116 | Created: String;
117 | LThread: TThread;
118 | LIntentService: TIntentServiceHelper;
119 | begin
120 | try
121 | //1 - Will result to restart the service with the same intent
122 | Result := TJService.JavaClass.START_REDELIVER_INTENT;
123 | LIntentService := TIntentServiceHelper.Create(Intent);
124 |
125 | if LIntentService.Data.IsEmpty then
126 | Exit;
127 |
128 | //2 - Create a thread to be inserted in the thread object list to let the service running without stopping
129 | LThread := TThread.CreateAnonymousThread(
130 | procedure
131 | var
132 | LEvent: TEvent;
133 | LJNotification: JNotification;
134 | begin
135 | try
136 | while True do
137 | begin
138 | //3 - Builds a notification and an event object
139 | LJNotification := FNotificationBuilder.build();
140 | LEvent := TEvent.Create;
141 |
142 | //4 - Subscribe to Ntfy
143 | Ntfy.Subscribe(LIntentService.Data, ShowUpNtfy);
144 |
145 | //5 - Starts as foreground and notifies
146 | TJService.Wrap(JavaService).startForeground(1, LJNotification, FOREGROUND_SERVICE_TYPE_DATA_SYNC);
147 | FNotificationManager.notify(1, LJNotification);
148 |
149 | //6 - Waits until infinite, so that the service can be kept running "forever"
150 | LEvent.WaitFor(INFINITE);
151 | JavaService.stopSelf(StartId);
152 | end;
153 | except
154 | on E: Exception do
155 | ShowUpNotification('Thread error: ' + sLineBreak + E.Message);
156 | end;
157 | end);
158 |
159 | // 7 - Insert and start the thread
160 | LThread.FreeOnTerminate := False;
161 | FThreads.Add(LThread);
162 | LThread.Start;
163 |
164 | except
165 | on E: Exception do
166 | ShowUpNotification('Start error: ' + sLineBreak + E.Message);
167 | end;
168 | end;
169 |
170 | procedure TDM.ShowUpNotification(const Text: String);
171 | var
172 | LNotification: TNotification;
173 | begin
174 | LNotification := NotificationCenter.CreateNotification();
175 | try
176 | LNotification.ChannelId := FChannelId;
177 | LNotification.Name := IntToStr(Random(1000));
178 | LNotification.Title := 'Ntfy Service';
179 | LNotification.AlertBody := Text;
180 | NotificationCenter.PresentNotification(LNotification);
181 | finally
182 | LNotification.Free;
183 | end;
184 | end;
185 |
186 | procedure TDM.ShowUpNtfy(AEvent: INotifyEvent);
187 | var
188 | LNotification: TNotification;
189 | begin
190 | if FWakeLock <> nil then
191 | FWakeLock.acquire;
192 |
193 | try
194 | LNotification := NotificationCenter.CreateNotification();
195 | try
196 | LNotification.ChannelId := FChannelId;
197 | LNotification.Name := IntToStr(Random(1000));
198 | LNotification.Title := AEvent.Title;
199 | LNotification.AlertBody := AEvent.MessageContent;
200 | NotificationCenter.PresentNotification(LNotification);
201 | finally
202 | LNotification.Free;
203 | end;
204 | finally
205 | if FWakeLock <> nil then
206 | if FWakeLock.isHeld then
207 | FWakeLock.release();
208 | end;
209 | end;
210 |
211 | end.
212 |
213 |
--------------------------------------------------------------------------------
/samples/android/src/Intent.Service.Helper.pas:
--------------------------------------------------------------------------------
1 | unit Intent.Service.Helper;
2 |
3 | interface
4 |
5 | uses
6 | AndroidApi.JNI.GraphicsContentViewText;
7 |
8 | type
9 | TIntentServiceHelper = record
10 | private
11 | FIntent: JIntent;
12 | FCode: Integer;
13 | FData: string;
14 | public
15 | class function Create(const Intent: JIntent): TIntentServiceHelper; overload; static;
16 | class function Create(const AServiceName: string; Code: Integer; Data: string): TIntentServiceHelper; overload; static;
17 | property Code: Integer read FCode;
18 | property Data: string read FData;
19 | property Intent: JIntent read FIntent;
20 | end;
21 |
22 | implementation
23 |
24 | uses
25 | System.SysUtils,
26 | Androidapi.Helpers,
27 | Androidapi.Jni,
28 | Androidapi.JNIBridge,
29 | Androidapi.JNI.JavaTypes;
30 |
31 | { TIntentServiceHelper }
32 |
33 | class function TIntentServiceHelper.Create(const Intent: JIntent): TIntentServiceHelper;
34 | begin
35 | Result.FCode := Intent.getIntExtra(TAndroidHelper.StringToJString('Code'), -1);
36 | Result.FData := TAndroidHelper.JStringToString(Intent.getStringExtra(TAndroidHelper.StringToJString('Data')));
37 | end;
38 |
39 | class function TIntentServiceHelper.Create(const AServiceName: string; Code: Integer; Data: string): TIntentServiceHelper;
40 | var
41 | LService: string;
42 | begin
43 | Result.FIntent := TJIntent.Create;
44 | LService := AServiceName;
45 | if not LService.StartsWith('com.embarcadero.services.') then
46 | LService := 'com.embarcadero.services.' + LService;
47 | Result.FIntent.setClassName(TAndroidHelper.Context.getPackageName(), TAndroidHelper.StringToJString(LService));
48 | Result.FIntent.putExtra(TAndroidHelper.StringToJString('Code'), Code);
49 | Result.FIntent.putExtra(TAndroidHelper.StringToJString('Data'), TAndroidHelper.StringToJString(Data));
50 | end;
51 |
52 | end.
53 |
--------------------------------------------------------------------------------
/samples/android/src/View.Main.fmx:
--------------------------------------------------------------------------------
1 | object Form1: TForm1
2 | Left = 0
3 | Top = 0
4 | Caption = 'V'
5 | ClientHeight = 641
6 | ClientWidth = 385
7 | Fill.Color = claNull
8 | Fill.Kind = Solid
9 | FormFactor.Width = 320
10 | FormFactor.Height = 480
11 | FormFactor.Devices = [Desktop]
12 | DesignerMasterStyle = 3
13 | object CornerButton1: TCornerButton
14 | StyledSettings = [Style]
15 | Position.X = 56.000000000000000000
16 | Position.Y = 380.000000000000000000
17 | Sides = [Top, Left, Bottom, Right]
18 | Size.Width = 273.000000000000000000
19 | Size.Height = 35.000000000000000000
20 | Size.PlatformDefault = False
21 | TabOrder = 1
22 | Text = 'Start Service (Subscribe)'
23 | TextSettings.Font.Family = 'Consolas'
24 | TextSettings.Font.Size = 11.000000000000000000
25 | TextSettings.FontColor = claWhitesmoke
26 | TextSettings.Trimming = None
27 | XRadius = 3.000000000000000000
28 | YRadius = 3.000000000000000000
29 | OnClick = CornerButton1Click
30 | end
31 | object CornerButton2: TCornerButton
32 | StyledSettings = [Style]
33 | Position.X = 56.000000000000000000
34 | Position.Y = 337.000000000000000000
35 | Sides = [Top, Left, Bottom, Right]
36 | Size.Width = 273.000000000000000000
37 | Size.Height = 35.000000000000000000
38 | Size.PlatformDefault = False
39 | TabOrder = 0
40 | Text = 'Send Notification (Ntfy)'
41 | TextSettings.Font.Family = 'Consolas'
42 | TextSettings.Font.Size = 11.000000000000000000
43 | TextSettings.FontColor = claWhitesmoke
44 | TextSettings.Trimming = None
45 | XRadius = 3.000000000000000000
46 | YRadius = 3.000000000000000000
47 | OnClick = CornerButton2Click
48 | end
49 | object Text1: TText
50 | Position.X = 96.000000000000000000
51 | Position.Y = 217.000000000000000000
52 | Size.Width = 193.000000000000000000
53 | Size.Height = 48.000000000000000000
54 | Size.PlatformDefault = False
55 | Text = 'Inform topic to subscribe, start service and close the app.'
56 | TextSettings.FontColor = claWhitesmoke
57 | end
58 | object Edit1: TEdit
59 | Touch.InteractiveGestures = [LongTap, DoubleTap]
60 | StyleLookup = 'editstyle'
61 | TabOrder = 3
62 | Text = 'your-very-secret-topic'
63 | TextSettings.Font.Family = 'Consolas'
64 | TextSettings.FontColor = claWhitesmoke
65 | TextSettings.HorzAlign = Center
66 | Position.X = 56.000000000000000000
67 | Position.Y = 281.000000000000000000
68 | Size.Width = 273.000000000000000000
69 | Size.Height = 32.000000000000000000
70 | Size.PlatformDefault = False
71 | StyledSettings = [Style]
72 | end
73 | object SkSvg1: TSkSvg
74 | Position.X = 168.000000000000000000
75 | Position.Y = 144.000000000000000000
76 | Svg.Source =
77 | ' '
123 | object Spin: TFloatAnimation
124 | AnimationType = InOut
125 | Delay = 0.200000002980232200
126 | Duration = 0.800000011920929000
127 | Interpolation = Exponential
128 | PropertyName = 'RotationAngle'
129 | StartValue = 0.000000000000000000
130 | StopValue = 360.000000000000000000
131 | end
132 | end
133 | object Text2: TText
134 | Align = Bottom
135 | Position.Y = 593.000000000000000000
136 | Size.Width = 385.000000000000000000
137 | Size.Height = 48.000000000000000000
138 | Size.PlatformDefault = False
139 | Text = 'Demo'
140 | TextSettings.Font.Size = 9.000000000000000000
141 | TextSettings.FontColor = claWhitesmoke
142 | end
143 | end
144 |
--------------------------------------------------------------------------------
/samples/android/src/View.Main.pas:
--------------------------------------------------------------------------------
1 | unit View.Main;
2 |
3 | interface
4 |
5 | uses
6 | System.Skia, FMX.Skia, System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants,
7 | FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs,
8 | FMX.Controls.Presentation, FMX.StdCtrls, FMX.Objects, FMX.Layouts,
9 | FMX.ExtCtrls, FMX.Edit, FMX.Ani;
10 |
11 | type
12 |
13 | TForm1 = class(TForm)
14 | CornerButton1: TCornerButton;
15 | CornerButton2: TCornerButton;
16 | Text1: TText;
17 | Edit1: TEdit;
18 | SkSvg1: TSkSvg;
19 | Spin: TFloatAnimation;
20 | Text2: TText;
21 | procedure StartService;
22 | procedure CornerButton1Click(Sender: TObject);
23 | procedure CornerButton2Click(Sender: TObject);
24 | private
25 | procedure Toast(const Text: String);
26 | end;
27 |
28 | var
29 | Form1: TForm1;
30 |
31 | implementation
32 |
33 | uses
34 | System.Android.Service,
35 | Intent.Service.Helper,
36 | AndroidAPI.Helpers,
37 | AndroidAPI.JNI.Widget,
38 | System.Threading,
39 | Notify, Androidapi.JNI.JavaTypes;
40 |
41 | {$R *.fmx}
42 |
43 | procedure TForm1.CornerButton1Click(Sender: TObject);
44 | begin
45 | StartService;
46 | end;
47 |
48 | procedure TForm1.CornerButton2Click(Sender: TObject);
49 | begin
50 | TTask.Create(procedure
51 | begin
52 |
53 | Ntfy.Notification(
54 | New.Notification
55 | .Topic(Edit1.Text)
56 | .Title('🧪 Android Test')
57 | .MessageContent('Should receive in this app')
58 | ).Publish;
59 |
60 | TThread.Synchronize(nil, procedure
61 | begin
62 | Spin.Start;
63 | Toast('Notification published successfully.');
64 | end);
65 | end).Start;
66 | end;
67 |
68 | procedure TForm1.StartService;
69 | var
70 | LIntentService: TIntentServiceHelper;
71 | begin
72 | try {serice name} { topic }
73 | LIntentService := TIntentServiceHelper.Create('NtfyServiceLocal', 0, Edit1.Text);
74 | TAndroidHelper.Activity.startForegroundService(LIntentService.Intent);
75 | TThread.Synchronize(nil, procedure
76 | begin
77 | Spin.Start;
78 | Toast('Intent sent successfully, you may close the app now.');
79 | end);
80 | except
81 | on E: Exception do
82 | ShowMessage(E.Message);
83 | end;
84 | end;
85 |
86 | procedure TForm1.Toast(const Text: String);
87 | var
88 | JText: JString;
89 | begin
90 | JText := StringToJString(Text);
91 | TJToast.JavaClass.makeText(
92 | TAndroidHelper.Context,
93 | TJCharSequence.Wrap(JText),
94 | TJToast.JavaClass.LENGTH_SHORT
95 | ).show;
96 | end;
97 |
98 | end.
99 |
--------------------------------------------------------------------------------
/samples/console/publisher/ConsolePublisher.dpr:
--------------------------------------------------------------------------------
1 | program ConsolePublisher;
2 |
3 | {$APPTYPE CONSOLE}
4 |
5 | uses
6 | Example.Basic in 'src\Example.Basic.pas',
7 | Example.Action.Header in 'src\Example.Action.Header.pas',
8 | Example.Priorities in 'src\Example.Priorities.pas',
9 | Example.Messages in 'src\Example.Messages.pas',
10 | Example.Emojis in 'src\Example.Emojis.pas',
11 | Example.Schedule.Delivery in 'src\Example.Schedule.Delivery.pas',
12 | Example.Action.View in 'src\Example.Action.View.pas',
13 | Example.Action.HTTP in 'src\Example.Action.HTTP.pas',
14 | Example.Attachments in 'src\Example.Attachments.pas',
15 | Example.URL.Attachments in 'src\Example.URL.Attachments.pas',
16 | Example.Icons in 'src\Example.Icons.pas',
17 | Example.Email in 'src\Example.Email.pas',
18 | Example.Advanced.Configs in 'src\Example.Advanced.Configs.pas',
19 | Notify;
20 |
21 | begin
22 |
23 | UseSimpleMessage;
24 | //UsePriorities;
25 | //UseMessage;
26 | //UseTags;
27 | //UseActionView;
28 | //UseActionHTTP;
29 | //UseAttachments;
30 | //UseURLAttachments;
31 | //UseIcons;
32 | //UseEmail;
33 |
34 | Ntfy.Topic('notify-delphi-integration-8jh27d').Publish;
35 |
36 | end.
37 |
--------------------------------------------------------------------------------
/samples/console/publisher/ConsolePublisher.res:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hazzelnuts/ntfy-for-delphi/fd0d51efafd6954bfd1aaaae98fc07531149bb6a/samples/console/publisher/ConsolePublisher.res
--------------------------------------------------------------------------------
/samples/console/publisher/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | # Console Publisher Sample
8 |
9 |
10 |
11 |
12 | Simple message
13 |
14 | ``` pascal
15 | Ntfy.Notification(
16 | New.Notification
17 | .Title('Simple message sent')
18 | .MessageContent('A message body...')
19 | );
20 | ```
21 |
22 | Setup Priority Levels
23 |
24 | ``` pascal
25 | Ntfy.Notification(
26 | New.Notification
27 | .Priority(TNotifyPriority.HIGH)
28 | );
29 | ```
30 |
31 | Emojis/Tags
32 |
33 | ``` pascal
34 | Ntfy.Notification(
35 | New.Notification
36 | .Tags(TArray.Create('partying_face', 'warning', 'rotating_light'))
37 | );
38 | ```
39 |
40 | Actions Buttons (view)
41 |
42 | ``` pascal
43 | Ntfy.Notification(New.Notification
44 | .Action(New.Action
45 | .&Type(TNotifyActionType.VIEW)
46 | .Url('geo:40.765819,-73.975866')
47 | .&Label('Open Google Maps'))
48 | );
49 | ```
50 |
51 | Actions Buttons (http)
52 |
53 | ``` pascal
54 | Ntfy.Notification(New.Notification
55 | .Action(New.Action
56 | .&Type(TNotifyActionType.HTTP)
57 | .Url('https://viacep.com.br/ws/01001000/json/')
58 | .&Label('Send GET request to viacep')
59 | .Method('GET'))
60 | );
61 | ```
62 |
63 | File attachments
64 |
65 | ``` pascal
66 | Ntfy.Notification(
67 | New.Notification
68 | .FilePath('..\img\delphi-notify.png')
69 | );
70 | ```
71 |
72 | URL attachments
73 |
74 | ``` pascal
75 | Ntfy.Notification(
76 | New.Notification
77 | .Attach('https://i.picsum.photos/id/1002/200/200.jpg')
78 | );
79 | ```
80 |
81 | URL icons attachments
82 |
83 | ``` pascal
84 | Ntfy.Notification(
85 | New.Notification
86 | .Icon('https://communityIcon_xnt6chtnr2j21.png')
87 | );
88 | ```
89 |
90 | Email notifications
91 |
92 | ``` pascal
93 | Ntfy.Notification(
94 | New.Notification
95 | .Title('Email test')
96 | .MessageContent('Emails with a title and a message')
97 | .Email('someemail@gmail.com')
98 | );
99 | ```
100 |
101 | Basic Logs
102 |
103 | ``` pascal
104 | Ntfy.SaveLog(True).LogPath('..\logs_folder');
105 | ```
106 |
107 | Other ntfy API configs
108 |
109 | ``` pascal
110 | // Username and password for protected topics
111 | // Warning! Username and password are not
112 | // encrypted, only encoded! Use HTTPS. You can
113 | // use this option when you are self-hosting
114 | Ntfy.UserName('username').Password('password');
115 |
116 | // Disabling Firebase will significantly increase
117 | // the amount of time messages are delivered
118 | // in Android
119 | Ntfy.DisableFireBase(True);
120 |
121 | // Disabling cache will cause messages no longer
122 | // to be delivered to unsubscribed clients
123 | Ntfy.Cache(False);
124 |
125 | // You can delay messages up to three days
126 | Ntfy.Delay('4h');
127 |
128 | ```
129 |
--------------------------------------------------------------------------------
/samples/console/publisher/src/Example.Action.HTTP.pas:
--------------------------------------------------------------------------------
1 | unit Example.Action.HTTP;
2 |
3 | interface
4 |
5 | ///
6 | /// Example using action view
7 | ///
8 |
9 | procedure UseActionHTTP;
10 |
11 | implementation
12 |
13 | uses
14 | Notify;
15 |
16 | procedure UseActionHTTP;
17 | begin
18 |
19 | Ntfy.Notification(New.Notification
20 | .Topic('your-very-secret-topic')
21 | .Action(New.Action
22 | .&Type(TNotifyActionType.HTTP)
23 | .Url('https://viacep.com.br/ws/01001000/json/')
24 | .&Label('Send GET request to viacep')
25 | .Method('GET'))
26 | );
27 |
28 | end;
29 |
30 | end.
31 |
--------------------------------------------------------------------------------
/samples/console/publisher/src/Example.Action.Header.pas:
--------------------------------------------------------------------------------
1 | unit Example.Action.Header;
2 |
3 | interface
4 |
5 | ///
6 | /// Example using ation headers
7 | ///
8 |
9 | procedure UseActionHeader;
10 |
11 | implementation
12 |
13 | uses
14 | Notify,
15 | Notify.Custom.Types,
16 | REST.Json.Types;
17 |
18 | procedure UseActionHeader;
19 | var
20 | Headers: TNotifyActionHeaders;
21 | begin
22 | Headers := TNotifyActionHeaders.Create;
23 | try
24 | Headers.Auth := 'Bearer 982j38sdfhj2181jcznxc81234b9as-i34';
25 | Headers.Cmd := 'OPEN XOAUTH .\crs\main\lib';
26 |
27 | Ntfy.Notification(New.Notification
28 | .Action(New.Action
29 | .&Type(TNotifyActionType.HTTP)
30 | .&Label('Action Headers')
31 | .Url('http://someurl.com')
32 | .Body('"message":"hello"')
33 | .Headers(Headers))
34 | );
35 |
36 | finally
37 | Headers.Free;
38 | end;
39 | end;
40 |
41 | end.
42 |
--------------------------------------------------------------------------------
/samples/console/publisher/src/Example.Action.View.pas:
--------------------------------------------------------------------------------
1 | unit Example.Action.View;
2 |
3 | interface
4 |
5 | ///
6 | /// Example using action view
7 | ///
8 |
9 | procedure UseActionView;
10 |
11 | implementation
12 |
13 | uses
14 | Notify;
15 |
16 | procedure UseActionView;
17 | begin
18 |
19 | // Opens Google Maps in a certain location
20 | Ntfy.Notification(New.Notification
21 | .Topic('your-very-secret-topic')
22 | .Action(New.Action
23 | .&Type(TNotifyActionType.VIEW)
24 | .Url('geo:40.765819,-73.975866')
25 | .&Label('Open Google Maps'))
26 | );
27 |
28 | // Opens gmail
29 | Ntfy.Notification(New.Notification
30 | .Topic('your-very-secret-topic')
31 | .Action(New.Action
32 | .&Type(TNotifyActionType.VIEW)
33 | .Url('mailto:phil@example.com')
34 | .&Label('Send Mail'))
35 | );
36 |
37 | end;
38 |
39 | end.
40 |
--------------------------------------------------------------------------------
/samples/console/publisher/src/Example.Advanced.Configs.pas:
--------------------------------------------------------------------------------
1 | unit Example.Advanced.Configs;
2 |
3 | interface
4 |
5 | ///
6 | /// Example for self-hosted apis
7 | ///
8 |
9 | procedure UseAdvancedConfigs;
10 |
11 | implementation
12 |
13 | uses
14 | Notify;
15 |
16 | procedure UseAdvancedConfigs;
17 | begin
18 |
19 | // Username and password for protected topics
20 | // Be careful! Username and password are not encrypted, only encoded! Use HTTPS
21 | Ntfy.UserName('username').Password('password');
22 |
23 | // Disabling Firebase will significantly increase the amount of time messages
24 | // are delivered in Android
25 | Ntfy.DisableFireBase(True);
26 |
27 | //Disabling cache will cause messages not to be delivered to whom is not subscribed
28 | Ntfy.Cache(False);
29 |
30 | end;
31 |
32 | end.
33 |
--------------------------------------------------------------------------------
/samples/console/publisher/src/Example.Attachments.pas:
--------------------------------------------------------------------------------
1 | unit Example.Attachments;
2 |
3 | interface
4 |
5 | ///
6 | /// Example using file attachments
7 | ///
8 |
9 | procedure UseAttachments;
10 |
11 | implementation
12 |
13 | uses
14 | System.IOUtils,
15 | System.SysUtils,
16 | Notify;
17 |
18 | procedure UseAttachments;
19 | var
20 | LFilePath: String;
21 | begin
22 |
23 | LFilePath := '..\..\..\..\img\delphi-notify.png';
24 |
25 | Ntfy.Notification(
26 | New.Notification
27 | .Topic('your-very-secret-topic')
28 | .AttachFile(LFilePath)
29 | );
30 |
31 | end;
32 |
33 | end.
34 |
--------------------------------------------------------------------------------
/samples/console/publisher/src/Example.Basic.pas:
--------------------------------------------------------------------------------
1 | unit Example.Basic;
2 |
3 | interface
4 |
5 | ///
6 | /// Publishing a simple message
7 | ///
8 |
9 | procedure UseSimpleMessage;
10 |
11 | implementation
12 |
13 | uses
14 | Notify;
15 |
16 | procedure UseSimpleMessage;
17 | begin
18 |
19 | Ntfy.Notification(
20 | New.Notification
21 | .Topic('your-very-secret-topic')
22 | .Title('Simple message sent')
23 | );
24 |
25 | end;
26 |
27 | end.
28 |
--------------------------------------------------------------------------------
/samples/console/publisher/src/Example.Email.pas:
--------------------------------------------------------------------------------
1 | unit Example.Email;
2 |
3 | interface
4 |
5 | ///
6 | /// Example using email
7 | ///
8 |
9 | procedure UseEmail;
10 |
11 | implementation
12 |
13 | uses
14 | Notify;
15 |
16 | procedure UseEmail;
17 | begin
18 |
19 | Ntfy.Notification(
20 | New.Notification
21 | .Topic('your-very-secret-topic')
22 | .Title('Email test')
23 | .MessageContent('Emails with a title and a message')
24 | .Email('someemail@gmail.com')
25 | );
26 |
27 | end;
28 |
29 | end.
30 |
--------------------------------------------------------------------------------
/samples/console/publisher/src/Example.Emojis.pas:
--------------------------------------------------------------------------------
1 | unit Example.Emojis;
2 |
3 | interface
4 |
5 | ///
6 | /// Example using emojis/tags
7 | ///
8 |
9 | procedure UseTags;
10 |
11 | implementation
12 |
13 | uses
14 | Notify;
15 |
16 | procedure UseTags;
17 | begin
18 |
19 | Ntfy.Notification(
20 | New.Notification
21 | .Topic('your-very-secret-topic')
22 | .Tags(TArray.Create('partying_face', 'warning', 'rotating_light'))
23 | );
24 |
25 | end;
26 |
27 | end.
28 |
--------------------------------------------------------------------------------
/samples/console/publisher/src/Example.Icons.pas:
--------------------------------------------------------------------------------
1 | unit Example.Icons;
2 |
3 | interface
4 |
5 | ///
6 | /// Example using icons url
7 | ///
8 |
9 | procedure UseIcons;
10 |
11 | implementation
12 |
13 | uses
14 | Notify;
15 |
16 | procedure UseIcons;
17 | begin
18 |
19 | Ntfy.Notification(
20 | New.Notification
21 | .Topic('your-very-secret-topic')
22 | .Icon('https://styles.redditmedia.com/t5_32uhe/styles/communityIcon_xnt6chtnr2j21.png')
23 | );
24 |
25 | end;
26 |
27 |
28 | end.
29 |
--------------------------------------------------------------------------------
/samples/console/publisher/src/Example.Messages.pas:
--------------------------------------------------------------------------------
1 | unit Example.Messages;
2 |
3 | interface
4 |
5 | ///
6 | /// Attaching a message body
7 | ///
8 |
9 | procedure UseMessage;
10 |
11 | implementation
12 |
13 | uses
14 | Notify;
15 |
16 | procedure UseMessage;
17 | begin
18 |
19 | Ntfy.Notification(
20 | New.Notification
21 | .Topic('your-very-secret-topic')
22 | .MessageContent('A message body...')
23 | );
24 |
25 | end;
26 |
27 | end.
28 |
--------------------------------------------------------------------------------
/samples/console/publisher/src/Example.Priorities.pas:
--------------------------------------------------------------------------------
1 | unit Example.Priorities;
2 |
3 | interface
4 |
5 | ///
6 | /// Example using priorities
7 | ///
8 |
9 | procedure UsePriorities;
10 |
11 | implementation
12 |
13 | uses
14 | Notify;
15 |
16 |
17 | procedure UsePriorities;
18 | begin
19 |
20 | Ntfy.Notification(
21 | New.Notification
22 | .Topic('your-very-secret-topic')
23 | .Priority(TNotifyPriority.HIGH)
24 | );
25 |
26 | end;
27 |
28 | end.
29 |
--------------------------------------------------------------------------------
/samples/console/publisher/src/Example.Schedule.Delivery.pas:
--------------------------------------------------------------------------------
1 | unit Example.Schedule.Delivery;
2 |
3 | interface
4 |
5 | ///
6 | /// Example scheduling delivery
7 | ///
8 |
9 | procedure UseScheduleDelivery;
10 |
11 | implementation
12 |
13 | uses
14 | Notify;
15 |
16 | procedure UseScheduleDelivery;
17 | begin
18 |
19 | Ntfy.Notification(
20 | New.Notification
21 | .Topic('your-very-secret-topic')
22 | .Delay('10s')
23 | );
24 |
25 | end;
26 |
27 | end.
28 |
--------------------------------------------------------------------------------
/samples/console/publisher/src/Example.URL.Attachments.pas:
--------------------------------------------------------------------------------
1 | unit Example.URL.Attachments;
2 |
3 | interface
4 |
5 | ///
6 | /// Example using url attachments
7 | ///
8 |
9 | procedure UseURLAttachments;
10 |
11 | implementation
12 |
13 | uses
14 | Notify, System.SysUtils;
15 |
16 | const
17 | AttachURL =
18 | 'https://images.unsplash.com/photo-1670531910262-'+
19 | '5ddb5ad666ea?crop=entropy&cs=tinysrgb&fit=crop&f'+
20 | 'm=jpg&h=150&ixid=MnwxfDB8MXxyYW5kb218MHx8fHx8fHx'+
21 | '8MTY3MTE0OTI2NA&ixlib=rb-4.0.3&q=80&w=500';
22 |
23 | procedure UseURLAttachments;
24 | begin
25 |
26 | Ntfy.Notification(
27 | New.Notification
28 | .Topic('your-very-secret-topic')
29 | .AttachURL(Trim(AttachURL))
30 | );
31 |
32 | end;
33 |
34 | end.
35 |
--------------------------------------------------------------------------------
/samples/console/subscriber/ConsoleSubscriber.dpr:
--------------------------------------------------------------------------------
1 | program ConsoleSubscriber;
2 |
3 | {$APPTYPE CONSOLE}
4 |
5 | {$R *.res}
6 |
7 | uses
8 | Notify;
9 |
10 | begin
11 |
12 | ReportMemoryLeaksOnShutdown := True;
13 |
14 | Ntfy.Subscribe('notify-delphi-integration-8jh27d',
15 | procedure (AEvent: INotifyEvent)
16 | begin
17 | Writeln('You received a message:');
18 | Writeln('Title: ' + AEvent.Title);
19 | Writeln('Message: ' + AEvent.MessageContent);
20 | end);
21 |
22 | end.
23 |
--------------------------------------------------------------------------------
/samples/console/subscriber/ConsoleSubscriber.res:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hazzelnuts/ntfy-for-delphi/fd0d51efafd6954bfd1aaaae98fc07531149bb6a/samples/console/subscriber/ConsoleSubscriber.res
--------------------------------------------------------------------------------
/samples/console/subscriber/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | # Subscriber Console Sample
8 |
9 |
10 |
11 | ## Subscribe to a topic
12 |
13 | Subscription is event based.
14 |
15 | ``` pascal
16 | Ntfy.Subscribe('your-very-secret-topic',
17 | procedure (AEvent: INotifyEvent);
18 | begin
19 | WriteLn('You received a message: ' + AEvent.MessageContent)
20 | end);
21 |
22 | ```
23 |
24 | Short poll
25 |
26 | ``` pascal
27 | Ntfy.Poll(True);
28 | Ntfy.Subscribe('your-very-secret-topic', CallBack);
29 | ```
30 |
31 | Fetching (duration, unix time or id)
32 |
33 | ``` pascal
34 | Ntfy.Since('2hs')
35 | Ntfy.Subscribe('your-very-secret-topic', CallBack);
36 | ```
37 |
38 |
39 | Scheduled only
40 |
41 | ``` pascal
42 | Ntfy.Scheduled(True)
43 | Ntfy.Subscribe('your-very-secret-topic', CallBack);
44 | ```
45 |
46 | ## Filtering
47 |
48 | ``` pascal
49 | Ntfy.Filter(TNotifyFilter.ID, 'xY77dh389');
50 | Ntfy.Filter(TNotifyFilter.TITLE, 'some title');
51 | Ntfy.Filter(TNotifyFilter.MESSAGECONTENT, 'some text');
52 | Ntfy.Filter(TNotifyFilter.TAGS, 'some text');
53 | Ntfy.Filter(TNotifyFilter.PRIORITY, IntToStr(Ord(TNotifyPriority.MAX)));
54 | ```
--------------------------------------------------------------------------------
/samples/openssl/README.md:
--------------------------------------------------------------------------------
1 | # SSL Libs
2 | If you are using Indy, paste these libs in the same directory where your app is running. Don't forget to add ```NTFY_HTTP_INDY``` in the conditional defines.
--------------------------------------------------------------------------------
/samples/openssl/lib.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hazzelnuts/ntfy-for-delphi/fd0d51efafd6954bfd1aaaae98fc07531149bb6a/samples/openssl/lib.zip
--------------------------------------------------------------------------------
/samples/openssl/lib32Bits.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hazzelnuts/ntfy-for-delphi/fd0d51efafd6954bfd1aaaae98fc07531149bb6a/samples/openssl/lib32Bits.zip
--------------------------------------------------------------------------------
/samples/openssl/lib64Bits.zip:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hazzelnuts/ntfy-for-delphi/fd0d51efafd6954bfd1aaaae98fc07531149bb6a/samples/openssl/lib64Bits.zip
--------------------------------------------------------------------------------
/samples/vcl/publisher/Ntfy-54x.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hazzelnuts/ntfy-for-delphi/fd0d51efafd6954bfd1aaaae98fc07531149bb6a/samples/vcl/publisher/Ntfy-54x.ico
--------------------------------------------------------------------------------
/samples/vcl/publisher/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # Publisher VCL Sample
4 |
5 |
6 |
7 | Compile and explore the VCL sample. Also check this [link](https://github.com/p-samuel/delphi-notify/tree/dev-psamuel/sample/console/publisher) for other resources.
8 |
9 |
10 |
11 |
12 |
13 | ## Self-hosted server (optional for this sample)
14 | Install Docker on your machine and run these steps:
15 |
16 | ``` cmd
17 | cd delphi-notify\tests
18 | docker compose create
19 | docker cp server.yml ntfy:/etc/ntfy
20 | docker compose up
21 | ```
--------------------------------------------------------------------------------
/samples/vcl/publisher/VCLPublisher.dpr:
--------------------------------------------------------------------------------
1 | program VCLPublisher;
2 |
3 | uses
4 | Vcl.Forms,
5 | View.Main in 'src\View.Main.pas' {ViewMain},
6 | Vcl.Themes,
7 | Vcl.Styles;
8 |
9 | {$R *.res}
10 |
11 | begin
12 | Application.Initialize;
13 | Application.MainFormOnTaskbar := True;
14 | TStyleManager.TrySetStyle('Glow');
15 | Application.CreateForm(TViewMain, ViewMain);
16 | Application.Run;
17 | end.
18 |
--------------------------------------------------------------------------------
/samples/vcl/publisher/VCLPublisher.res:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hazzelnuts/ntfy-for-delphi/fd0d51efafd6954bfd1aaaae98fc07531149bb6a/samples/vcl/publisher/VCLPublisher.res
--------------------------------------------------------------------------------
/samples/vcl/publisher/VCLPublisher_Icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hazzelnuts/ntfy-for-delphi/fd0d51efafd6954bfd1aaaae98fc07531149bb6a/samples/vcl/publisher/VCLPublisher_Icon.ico
--------------------------------------------------------------------------------
/samples/vcl/publisher/img/lego-wars.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hazzelnuts/ntfy-for-delphi/fd0d51efafd6954bfd1aaaae98fc07531149bb6a/samples/vcl/publisher/img/lego-wars.jpg
--------------------------------------------------------------------------------
/samples/vcl/publisher/img/orion.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hazzelnuts/ntfy-for-delphi/fd0d51efafd6954bfd1aaaae98fc07531149bb6a/samples/vcl/publisher/img/orion.jpg
--------------------------------------------------------------------------------
/samples/vcl/publisher/img/vcl-sample.PNG:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hazzelnuts/ntfy-for-delphi/fd0d51efafd6954bfd1aaaae98fc07531149bb6a/samples/vcl/publisher/img/vcl-sample.PNG
--------------------------------------------------------------------------------
/samples/vcl/publisher/src/Example.Notification.pas:
--------------------------------------------------------------------------------
1 | unit Example.Notification;
2 |
3 | interface
4 |
5 | procedure SendNotification;
6 |
7 | implementation
8 |
9 | uses
10 | Notify;
11 |
12 |
13 | procedure SendNotification;
14 | var
15 | LTags: TArray;
16 | LNotification: INotifyNotification;
17 | begin
18 | LNotification := New.Notification;
19 | LNotification.Topic(CbTopic.Text);
20 | LNotification.Title(lbeTitle.Text);
21 | LNotification.MessageContent(lbeMessage.Text);
22 | LNotification.Priority(TNotifyPriority(CbPriority.ItemIndex + 1));
23 | LNotification.Icon(lbeIconAttachment.Text);
24 | LNotification.FilePath(lbeFileAttachment.Text);
25 | LNotification.Attach(lbeURLAttachment.Text);
26 | LNotification.Email(lbeEmail.Text);
27 | Ntfy.Notification(LNotification);
28 | Ntfy.Publish;
29 | end;
30 |
31 | end.
32 |
--------------------------------------------------------------------------------
/samples/vcl/publisher/src/View.Main.pas:
--------------------------------------------------------------------------------
1 | unit View.Main;
2 |
3 | interface
4 |
5 | uses
6 | Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
7 | Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Vcl.ExtCtrls,
8 | Vcl.Imaging.pngimage, Notify, Vcl.CheckLst, Data.DB, Vcl.Grids, Vcl.DBGrids,
9 | FireDAC.Stan.Intf, FireDAC.Stan.Option, FireDAC.Stan.Param,
10 | FireDAC.Stan.Error, FireDAC.DatS, FireDAC.Phys.Intf, FireDAC.DApt.Intf,
11 | FireDAC.Comp.DataSet, FireDAC.Comp.Client, Vcl.Mask;
12 |
13 | type
14 | TViewMain = class(TForm)
15 | gpInfo: TGroupBox;
16 | gbActions: TGroupBox;
17 | CbPriority: TComboBox;
18 | lbPriority: TLabel;
19 | Label1: TLabel;
20 | lbeFileAttachment: TLabeledEdit;
21 | btnFileAttachment: TButton;
22 | lbeURLAttachment: TLabeledEdit;
23 | lbeIconAttachment: TLabeledEdit;
24 | FileDialog: TOpenDialog;
25 | CbActionType: TComboBox;
26 | lblActionType: TLabel;
27 | CkActionClear: TCheckBox;
28 | CbActionMethod: TComboBox;
29 | lbActionMethod: TLabel;
30 | MemActionBody: TMemo;
31 | lblActionBody: TLabel;
32 | DBGrid1: TDBGrid;
33 | btnAddAction: TButton;
34 | btnDeleteAction: TButton;
35 | TableActions: TFDMemTable;
36 | TableActionsTYPE: TIntegerField;
37 | DsTableAction: TDataSource;
38 | TableActionsMETHOD: TStringField;
39 | TableActionsCLEAR: TBooleanField;
40 | TableActionsBODY: TMemoField;
41 | lbeEmail: TLabeledEdit;
42 | lblTopic: TLabel;
43 | lbeTitle: TLabeledEdit;
44 | CbTopic: TComboBox;
45 | lbeActionLabel: TLabeledEdit;
46 | lbeActionURL: TLabeledEdit;
47 | TableActionsLABEL: TStringField;
48 | TableActionsURL: TStringField;
49 | lbeDelay: TLabeledEdit;
50 | btnPublish: TButton;
51 | LbeBaseURL: TLabeledEdit;
52 | MemMessage: TMemo;
53 | LbMessage: TLabel;
54 | LbeUsername: TLabeledEdit;
55 | LbePassword: TLabeledEdit;
56 | CkDisableFirebase: TCheckBox;
57 | CkCache: TCheckBox;
58 | MemTags: TMemo;
59 | procedure FormCreate(Sender: TObject);
60 | procedure btnFileAttachmentClick(Sender: TObject);
61 | procedure CbActionTypeChange(Sender: TObject);
62 | procedure btnAddActionClick(Sender: TObject);
63 | procedure btnDeleteActionClick(Sender: TObject);
64 | published
65 | procedure btnPublishClick(Sender: TObject);
66 | private
67 | FNotification: INotifyNotification;
68 | procedure SendNotification;
69 | procedure AddActions;
70 | end;
71 |
72 | var
73 | ViewMain: TViewMain;
74 |
75 | implementation
76 |
77 | uses
78 | System.Threading;
79 |
80 | {$R *.dfm}
81 |
82 | procedure TViewMain.AddActions;
83 | begin
84 | if not TableActions.IsEmpty then
85 | begin
86 | TableActions.First;
87 | while not TableActions.Eof do
88 | begin
89 | FNotification.Action(New.Action
90 | .&Label(TableActionsLABEL.AsString)
91 | .&Url(TableActionsURL.AsString)
92 | .Method(TableActionsMETHOD.AsString)
93 | .Clear(TableActionsCLEAR.AsBoolean)
94 | .&Type(TNotifyActionType(Ord(TableActionsTYPE.AsInteger)))
95 | .Body(TableActionsBODY.GetAsString)
96 | );
97 | TableActions.Next;
98 | end;
99 | end;
100 | end;
101 |
102 | procedure TViewMain.btnAddActionClick(Sender: TObject);
103 | begin
104 |
105 | if TableActions.RecordCount >= 3 then
106 | Exit;
107 |
108 | TableActions.Open;
109 |
110 | TableActions.AppendRecord([
111 | Ord(TNotifyActionType(CbActionType.ItemIndex)),
112 | CbActionMethod.Text,
113 | CkActionClear.Checked,
114 | MemActionBody.Lines,
115 | lbeActionLabel.Text,
116 | lbeActionURL.Text
117 | ])
118 | end;
119 |
120 | procedure TViewMain.btnDeleteActionClick(Sender: TObject);
121 | begin
122 | FNotification.ClearActions;
123 | if not TableActions.IsEmpty then
124 | TableActions.EmptyDataSet;
125 | end;
126 |
127 | procedure TViewMain.btnFileAttachmentClick(Sender: TObject);
128 | begin
129 | FileDialog.InitialDir := ExtractFileDir(Application.ExeName);
130 | if FileDialog.Execute then
131 | begin
132 | lbeFileAttachment.Text := FileDialog.FileName;
133 | end;
134 | end;
135 |
136 | procedure TViewMain.btnPublishClick(Sender: TObject);
137 | var
138 | LTask: ITask;
139 | begin
140 |
141 | if lbeTitle.Text = '' then
142 | Exit;
143 |
144 | LTask := TTask.Create(procedure begin
145 | try
146 | btnPublish.Enabled := False;
147 | btnPublish.Caption := '⌛ Notify';
148 | SendNotification;
149 | finally
150 | TThread.Queue(nil, procedure begin
151 | btnPublish.Caption := '✔ Notify';
152 | btnPublish.Enabled := True;
153 | end)
154 | end;
155 | end);
156 |
157 | LTask.Start;
158 | end;
159 |
160 | procedure TViewMain.CbActionTypeChange(Sender: TObject);
161 | begin
162 |
163 | if CbActionType.Text = 'view' then
164 | CbActionMethod.ItemIndex := 1;
165 |
166 | CbActionMethod.Enabled := (CbActionType.Text <> 'view');
167 | MemActionBody.Enabled := (CbActionType.Text <> 'view');
168 | lblActionBody.Enabled := (CbActionType.Text <> 'view');
169 |
170 | end;
171 |
172 | procedure TViewMain.FormCreate(Sender: TObject);
173 | begin
174 | FNotification := New.Notification;
175 | btnAddAction.Click;
176 | end;
177 |
178 | procedure TViewMain.SendNotification;
179 | begin
180 |
181 | FNotification := New.Notification;
182 |
183 | FNotification
184 | .Topic(CbTopic.Text)
185 | .Title(lbeTitle.Text)
186 | .MessageContent(MemMessage.Lines.Text)
187 | .Priority(TNotifyPriority(CbPriority.ItemIndex + 1))
188 | .Icon(lbeIconAttachment.Text)
189 | .AttachFile(lbeFileAttachment.Text)
190 | .AttachURL(lbeURLAttachment.Text)
191 | .Email(lbeEmail.Text)
192 | .Delay(lbeDelay.Text)
193 | .Tags(MemTags.Lines.ToStringArray);
194 |
195 | AddActions;
196 |
197 | Ntfy
198 | .UserName(LbeUsername.Text)
199 | .Password(LbePassword.Text)
200 | .BaseURL(LbeBaseURL.Text)
201 | .Topic(CbTopic.Text)
202 | .DisableFireBase(CkDisableFirebase.Checked)
203 | .Cache(CkCache.Checked)
204 | .Notification(FNotification).Publish;
205 |
206 | end;
207 |
208 | end.
209 |
--------------------------------------------------------------------------------
/samples/vcl/subscriber/Ntfy-54x.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hazzelnuts/ntfy-for-delphi/fd0d51efafd6954bfd1aaaae98fc07531149bb6a/samples/vcl/subscriber/Ntfy-54x.ico
--------------------------------------------------------------------------------
/samples/vcl/subscriber/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # Subscriber VCL Sample
4 |
5 |
6 |
7 | Compile and explore the VCL sample. Also check this [link](https://github.com/p-samuel/delphi-notify/tree/dev-psamuel/sample/console/publisher) for other resources.
8 |
9 |
10 |
11 |
12 |
13 |
14 | ## How to subscribe
15 |
16 | Subscription is event based.
17 |
18 | ``` pascal
19 | uses
20 | Notify;
21 |
22 | begin
23 | Ntfy.Subscribe('your-very-secret-topic',
24 | procedure (AEvent: INotifyEvent)
25 | begin
26 | ShowMessage('You received a message: ' + AEvent.Title)
27 | end);
28 | end;
29 | ```
30 |
31 | You can define your own callback procedure and use to perform other operations every time a notification is fired.
32 |
33 | ``` pascal
34 | uses
35 | Notify;
36 |
37 | procedure YourCallBackProcedure(AEvent: INotifyEvent);
38 | begin
39 | DoSomethingWith(AEvent);
40 | end;
41 |
42 | begin
43 | Ntfy.Subscribe('your-very-secret-topic', YourCallBackProcedure);
44 | end;
45 | ```
46 |
47 | ## ⚙ Self-hosted server (optional for this sample)
48 | Install Docker in your machine and run these commands after:
49 |
50 | ``` cmd
51 | cd delphi-notify\tests
52 | docker compose create
53 | docker cp server.yml ntfy:/etc/ntfy
54 | docker compose up
55 | ```
--------------------------------------------------------------------------------
/samples/vcl/subscriber/VCLSubscriber.Artwork/Windows/AppIcon.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hazzelnuts/ntfy-for-delphi/fd0d51efafd6954bfd1aaaae98fc07531149bb6a/samples/vcl/subscriber/VCLSubscriber.Artwork/Windows/AppIcon.icns
--------------------------------------------------------------------------------
/samples/vcl/subscriber/VCLSubscriber.Artwork/Windows/AppIcon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hazzelnuts/ntfy-for-delphi/fd0d51efafd6954bfd1aaaae98fc07531149bb6a/samples/vcl/subscriber/VCLSubscriber.Artwork/Windows/AppIcon.ico
--------------------------------------------------------------------------------
/samples/vcl/subscriber/VCLSubscriber.Artwork/Windows/Uwp_150.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hazzelnuts/ntfy-for-delphi/fd0d51efafd6954bfd1aaaae98fc07531149bb6a/samples/vcl/subscriber/VCLSubscriber.Artwork/Windows/Uwp_150.png
--------------------------------------------------------------------------------
/samples/vcl/subscriber/VCLSubscriber.Artwork/Windows/Uwp_44.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hazzelnuts/ntfy-for-delphi/fd0d51efafd6954bfd1aaaae98fc07531149bb6a/samples/vcl/subscriber/VCLSubscriber.Artwork/Windows/Uwp_44.png
--------------------------------------------------------------------------------
/samples/vcl/subscriber/VCLSubscriber.dpr:
--------------------------------------------------------------------------------
1 | program VCLSubscriber;
2 |
3 | uses
4 | Vcl.Forms,
5 | View.Main in 'src\View.Main.pas' {ViewMain},
6 | Example.Push.Notifications in 'src\Example.Push.Notifications.pas',
7 | Vcl.Themes,
8 | Vcl.Styles;
9 |
10 | {$R *.res}
11 |
12 | begin
13 | ReportMemoryLeaksOnShutdown := True;
14 | Application.Initialize;
15 | Application.MainFormOnTaskbar := True;
16 | TStyleManager.TrySetStyle('Glow');
17 | Application.CreateForm(TViewMain, ViewMain);
18 | Application.Run;
19 | end.
20 |
--------------------------------------------------------------------------------
/samples/vcl/subscriber/VCLSubscriber.res:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hazzelnuts/ntfy-for-delphi/fd0d51efafd6954bfd1aaaae98fc07531149bb6a/samples/vcl/subscriber/VCLSubscriber.res
--------------------------------------------------------------------------------
/samples/vcl/subscriber/img/subscriber-vcl.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hazzelnuts/ntfy-for-delphi/fd0d51efafd6954bfd1aaaae98fc07531149bb6a/samples/vcl/subscriber/img/subscriber-vcl.png
--------------------------------------------------------------------------------
/samples/vcl/subscriber/src/Example.Push.Notifications.pas:
--------------------------------------------------------------------------------
1 | unit Example.Push.Notifications;
2 |
3 | interface
4 |
5 | uses
6 | System.Notification,
7 | Notify;
8 |
9 | procedure PushWindowsNotification(AEvent: INotifyEvent);
10 |
11 | implementation
12 |
13 | procedure PushWindowsNotification(AEvent: INotifyEvent);
14 | var
15 | LNotificationCenter: TNotificationCenter;
16 | LNotification: TNotification;
17 | begin
18 | LNotificationCenter := TNotificationCenter.Create(nil);
19 | try
20 | LNotification := LNotificationCenter.CreateNotification();
21 | try
22 | LNotification.Name := AEvent.Id;
23 | LNotification.Title := AEvent.Title;
24 | LNotification.AlertBody := AEvent.MessageContent;
25 | LNotificationCenter.PresentNotification(LNotification);
26 | finally
27 | LNotification.Free;
28 | end;
29 | finally
30 | LNotificationCenter.Free;
31 | end;
32 | end;
33 |
34 | end.
35 |
--------------------------------------------------------------------------------
/samples/vcl/subscriber/src/View.Main.dfm:
--------------------------------------------------------------------------------
1 | object ViewMain: TViewMain
2 | Left = 960
3 | Top = 256
4 | Caption = 'Ntfy Subscriber'
5 | ClientHeight = 584
6 | ClientWidth = 724
7 | Color = clBtnFace
8 | Font.Charset = DEFAULT_CHARSET
9 | Font.Color = clWindowText
10 | Font.Height = -11
11 | Font.Name = 'Tahoma'
12 | Font.Style = []
13 | Position = poDesktopCenter
14 | OnClose = FormClose
15 | OnCreate = FormCreate
16 | DesignSize = (
17 | 724
18 | 584)
19 | TextHeight = 13
20 | object lblTopic: TLabel
21 | Left = 445
22 | Top = 13
23 | Width = 260
24 | Height = 13
25 | Caption = 'Topics - Break a line for each. Don'#39't leave empty lines.'
26 | end
27 | object BtnSubscribe: TButton
28 | Left = 16
29 | Top = 486
30 | Width = 132
31 | Height = 25
32 | Caption = 'Subscribe'
33 | TabOrder = 0
34 | OnClick = BtnSubscribeClick
35 | end
36 | object BtnUnsubscribe: TButton
37 | Left = 16
38 | Top = 517
39 | Width = 132
40 | Height = 25
41 | Caption = 'Unsubscribe'
42 | Enabled = False
43 | TabOrder = 1
44 | OnClick = BtnUnsubscribeClick
45 | end
46 | object DBGrid1: TDBGrid
47 | Left = 154
48 | Top = 138
49 | Width = 551
50 | Height = 433
51 | Anchors = [akLeft, akTop, akRight, akBottom]
52 | DataSource = DsTable
53 | Options = [dgTitles, dgIndicator, dgColumnResize, dgColLines, dgRowLines, dgTabs, dgRowSelect, dgConfirmDelete, dgCancelOnExit, dgTitleClick, dgTitleHotTrack]
54 | TabOrder = 2
55 | TitleFont.Charset = DEFAULT_CHARSET
56 | TitleFont.Color = clWindowText
57 | TitleFont.Height = -11
58 | TitleFont.Name = 'Tahoma'
59 | TitleFont.Style = []
60 | Columns = <
61 | item
62 | Expanded = False
63 | FieldName = 'ID'
64 | Width = 50
65 | Visible = True
66 | end
67 | item
68 | Expanded = False
69 | FieldName = 'TIME'
70 | Width = 118
71 | Visible = True
72 | end
73 | item
74 | Expanded = False
75 | FieldName = 'PRIORITY'
76 | Visible = True
77 | end
78 | item
79 | Expanded = False
80 | FieldName = 'TITLE'
81 | Width = 146
82 | Visible = True
83 | end
84 | item
85 | Expanded = False
86 | FieldName = 'MSG'
87 | Width = 700
88 | Visible = True
89 | end
90 | item
91 | Expanded = False
92 | FieldName = 'TOPIC'
93 | Width = 300
94 | Visible = True
95 | end>
96 | end
97 | object CkPoll: TCheckBox
98 | Left = 16
99 | Top = 137
100 | Width = 41
101 | Height = 17
102 | Caption = 'Poll'
103 | TabOrder = 3
104 | end
105 | object GbSince: TRadioGroup
106 | Left = 16
107 | Top = 17
108 | Width = 89
109 | Height = 71
110 | Caption = 'Since'
111 | Enabled = False
112 | ItemIndex = 2
113 | Items.Strings = (
114 | 'Message Id'
115 | 'Duration'
116 | 'Unix Time')
117 | TabOrder = 4
118 | OnClick = GbSinceClick
119 | end
120 | object CkScheduled: TCheckBox
121 | Left = 16
122 | Top = 155
123 | Width = 132
124 | Height = 17
125 | Caption = 'Fectch scheduled only'
126 | TabOrder = 5
127 | end
128 | object CkSince: TCheckBox
129 | Left = 16
130 | Top = 173
131 | Width = 132
132 | Height = 17
133 | Caption = 'Fetch cached messages'
134 | TabOrder = 6
135 | OnClick = CkSinceClick
136 | end
137 | object GbFilters: TGroupBox
138 | Left = 16
139 | Top = 253
140 | Width = 132
141 | Height = 225
142 | Caption = 'Filters'
143 | TabOrder = 7
144 | object lbPriority: TLabel
145 | Left = 15
146 | Top = 177
147 | Width = 34
148 | Height = 13
149 | Caption = 'Priority'
150 | end
151 | object lbeIdFilter: TLabeledEdit
152 | Left = 15
153 | Top = 31
154 | Width = 104
155 | Height = 21
156 | EditLabel.Width = 53
157 | EditLabel.Height = 13
158 | EditLabel.Caption = 'Message id'
159 | TabOrder = 0
160 | Text = ''
161 | TextHint = 'Id'
162 | end
163 | object lbeFilterTitle: TLabeledEdit
164 | Left = 15
165 | Top = 71
166 | Width = 104
167 | Height = 21
168 | EditLabel.Width = 65
169 | EditLabel.Height = 13
170 | EditLabel.Caption = 'Message Title'
171 | TabOrder = 1
172 | Text = ''
173 | TextHint = 'Title'
174 | end
175 | object lbeFilterMessage: TLabeledEdit
176 | Left = 15
177 | Top = 111
178 | Width = 104
179 | Height = 21
180 | EditLabel.Width = 67
181 | EditLabel.Height = 13
182 | EditLabel.Caption = 'Message Text'
183 | TabOrder = 2
184 | Text = ''
185 | TextHint = 'Message'
186 | end
187 | object CbFilterPriority: TComboBox
188 | Left = 15
189 | Top = 196
190 | Width = 104
191 | Height = 21
192 | Style = csDropDownList
193 | ItemIndex = 0
194 | TabOrder = 3
195 | Items.Strings = (
196 | ''
197 | 'Min'
198 | 'Low'
199 | 'Default'
200 | 'High'
201 | 'Maximum')
202 | end
203 | object lbeFilterTags: TLabeledEdit
204 | Left = 15
205 | Top = 150
206 | Width = 104
207 | Height = 21
208 | EditLabel.Width = 68
209 | EditLabel.Height = 13
210 | EditLabel.Caption = 'Message Tags'
211 | TabOrder = 4
212 | Text = ''
213 | TextHint = 'Tags'
214 | end
215 | end
216 | object BtnClearTable: TButton
217 | Left = 680
218 | Top = 107
219 | Width = 25
220 | Height = 25
221 | Caption = #9851
222 | TabOrder = 8
223 | OnClick = BtnClearTableClick
224 | end
225 | object LbeBaseURL: TLabeledEdit
226 | Left = 16
227 | Top = 111
228 | Width = 249
229 | Height = 21
230 | EditLabel.Width = 45
231 | EditLabel.Height = 13
232 | EditLabel.Caption = 'Base URL'
233 | TabOrder = 9
234 | Text = 'https://ntfy.sh'
235 | TextHint = 'Tags'
236 | end
237 | object MemTopics: TMemo
238 | Left = 462
239 | Top = 32
240 | Width = 243
241 | Height = 52
242 | BorderStyle = bsNone
243 | Lines.Strings = (
244 | 'your-very-secret-topic'
245 | 'notify-delphi-integration-8jh27d')
246 | TabOrder = 10
247 | end
248 | object BtnHide: TButton
249 | Left = 16
250 | Top = 548
251 | Width = 132
252 | Height = 25
253 | Caption = 'Hide'
254 | TabOrder = 11
255 | OnClick = BtnHideClick
256 | end
257 | object LbeUsername: TLabeledEdit
258 | Left = 271
259 | Top = 111
260 | Width = 132
261 | Height = 21
262 | EditLabel.Width = 48
263 | EditLabel.Height = 13
264 | EditLabel.Caption = 'Username'
265 | TabOrder = 12
266 | Text = ''
267 | TextHint = '(optional)'
268 | end
269 | object LbePassword: TLabeledEdit
270 | Left = 409
271 | Top = 111
272 | Width = 132
273 | Height = 21
274 | EditLabel.Width = 46
275 | EditLabel.Height = 13
276 | EditLabel.Caption = 'Password'
277 | PasswordChar = '*'
278 | TabOrder = 13
279 | Text = ''
280 | TextHint = '(optional)'
281 | end
282 | object DtSince: TDateTimePicker
283 | Left = 111
284 | Top = 63
285 | Width = 137
286 | Height = 21
287 | Date = 44911.000000000000000000
288 | Time = 0.858430219908768800
289 | TabOrder = 14
290 | end
291 | object EdtSince: TEdit
292 | Left = 111
293 | Top = 36
294 | Width = 137
295 | Height = 21
296 | TabOrder = 15
297 | TextHint = 'Message id or Unix time'
298 | end
299 | object CkShowNotification: TCheckBox
300 | Left = 16
301 | Top = 192
302 | Width = 132
303 | Height = 17
304 | Caption = 'Show up notifications'
305 | TabOrder = 16
306 | end
307 | object CkAutoSaveSubs: TCheckBox
308 | Left = 16
309 | Top = 211
310 | Width = 132
311 | Height = 17
312 | Caption = 'Auto Save Subs.'
313 | TabOrder = 17
314 | end
315 | object CkAutoSubscribe: TCheckBox
316 | Left = 17
317 | Top = 231
318 | Width = 132
319 | Height = 17
320 | Caption = 'Auto Subscribe'
321 | TabOrder = 18
322 | end
323 | object TableNotification: TFDMemTable
324 | FetchOptions.AssignedValues = [evMode]
325 | FetchOptions.Mode = fmAll
326 | ResourceOptions.AssignedValues = [rvSilentMode]
327 | ResourceOptions.SilentMode = True
328 | UpdateOptions.AssignedValues = [uvCheckRequired, uvAutoCommitUpdates]
329 | UpdateOptions.CheckRequired = False
330 | UpdateOptions.AutoCommitUpdates = True
331 | Left = 568
332 | Top = 336
333 | object TableNotificationID: TStringField
334 | DisplayLabel = 'Id'
335 | FieldName = 'ID'
336 | Size = 30
337 | end
338 | object TableNotificationTIME: TStringField
339 | DisplayLabel = 'Time'
340 | FieldName = 'TIME'
341 | end
342 | object TableNotificationPRIORITY: TStringField
343 | DisplayLabel = 'Priority'
344 | FieldName = 'PRIORITY'
345 | Size = 10
346 | end
347 | object TableNotificationTITLE: TStringField
348 | DisplayLabel = 'Title'
349 | FieldName = 'TITLE'
350 | Size = 50
351 | end
352 | object TableNotificationMSG: TStringField
353 | DisplayLabel = 'Message'
354 | FieldName = 'MSG'
355 | Size = 500
356 | end
357 | object TableNotificationTOPIC: TStringField
358 | DisplayLabel = 'Topic'
359 | FieldName = 'TOPIC'
360 | Size = 100
361 | end
362 | end
363 | object DsTable: TDataSource
364 | DataSet = TableNotification
365 | Left = 640
366 | Top = 336
367 | end
368 | object Tray: TTrayIcon
369 | PopupMenu = Pop
370 | Left = 392
371 | Top = 344
372 | end
373 | object Pop: TPopupMenu
374 | Left = 432
375 | Top = 344
376 | object PopShow: TMenuItem
377 | Caption = 'Show'
378 | OnClick = PopShowClick
379 | end
380 | object PopSubscribe: TMenuItem
381 | Caption = 'Subscribe'
382 | OnClick = PopSubscribeClick
383 | end
384 | object PopUnsubscribe: TMenuItem
385 | Caption = 'Unsubscribe'
386 | Enabled = False
387 | OnClick = PopUnsubscribeClick
388 | end
389 | object PopQuit: TMenuItem
390 | Caption = 'Quit'
391 | OnClick = PopQuitClick
392 | end
393 | end
394 | end
395 |
--------------------------------------------------------------------------------
/samples/vcl/subscriber/src/View.Main.pas:
--------------------------------------------------------------------------------
1 | unit View.Main;
2 |
3 | interface
4 |
5 | uses
6 | Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
7 | Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, Notify,
8 | System.Notification, Data.DB, FireDAC.Stan.Intf, FireDAC.Stan.Option,
9 | FireDAC.Stan.Param, FireDAC.Stan.Error, FireDAC.DatS, FireDAC.Phys.Intf,
10 | FireDAC.DApt.Intf, FireDAC.Comp.DataSet, FireDAC.Comp.Client, Vcl.Grids,
11 | Vcl.DBGrids, Vcl.Imaging.pngimage, Vcl.ExtCtrls, Vcl.ComCtrls, Vcl.CheckLst,
12 | Vcl.Menus, Vcl.Mask, System.IniFiles;
13 |
14 | type
15 | TViewMain = class(TForm)
16 | BtnSubscribe: TButton;
17 | BtnUnsubscribe: TButton;
18 | DBGrid1: TDBGrid;
19 | TableNotification: TFDMemTable;
20 | TableNotificationID: TStringField;
21 | TableNotificationTITLE: TStringField;
22 | TableNotificationMSG: TStringField;
23 | DsTable: TDataSource;
24 | CkPoll: TCheckBox;
25 | GbSince: TRadioGroup;
26 | CkScheduled: TCheckBox;
27 | CkSince: TCheckBox;
28 | GbFilters: TGroupBox;
29 | lbeIdFilter: TLabeledEdit;
30 | lbeFilterTitle: TLabeledEdit;
31 | lbeFilterMessage: TLabeledEdit;
32 | CbFilterPriority: TComboBox;
33 | lbPriority: TLabel;
34 | lbeFilterTags: TLabeledEdit;
35 | lblTopic: TLabel;
36 | BtnClearTable: TButton;
37 | LbeBaseURL: TLabeledEdit;
38 | MemTopics: TMemo;
39 | Tray: TTrayIcon;
40 | Pop: TPopupMenu;
41 | PopShow: TMenuItem;
42 | PopSubscribe: TMenuItem;
43 | PopUnsubscribe: TMenuItem;
44 | BtnHide: TButton;
45 | LbeUsername: TLabeledEdit;
46 | LbePassword: TLabeledEdit;
47 | PopQuit: TMenuItem;
48 | DtSince: TDateTimePicker;
49 | EdtSince: TEdit;
50 | TableNotificationTIME: TStringField;
51 | TableNotificationTOPIC: TStringField;
52 | TableNotificationPRIORITY: TStringField;
53 | CkShowNotification: TCheckBox;
54 | CkAutoSaveSubs: TCheckBox;
55 | CkAutoSubscribe: TCheckBox;
56 | procedure BtnSubscribeClick(Sender: TObject);
57 | procedure BtnUnsubscribeClick(Sender: TObject);
58 | procedure GbSinceClick(Sender: TObject);
59 | procedure CkSinceClick(Sender: TObject);
60 | procedure BtnClearTableClick(Sender: TObject);
61 | procedure BtnHideClick(Sender: TObject);
62 | procedure CkFiltersExit(Sender: TObject);
63 | procedure PopQuitClick(Sender: TObject);
64 | procedure PopShowClick(Sender: TObject);
65 | procedure PopSubscribeClick(Sender: TObject);
66 | procedure PopUnsubscribeClick(Sender: TObject);
67 | procedure FormCreate(Sender: TObject);
68 | procedure FormClose(Sender: TObject; var Action: TCloseAction);
69 | private
70 | FTopics: String;
71 | FSince: String;
72 | FIniFileName: String;
73 | procedure CheckTopics;
74 | procedure CheckSince;
75 | procedure CheckButtons;
76 | procedure CheckPops;
77 | function CheckPriority: String;
78 | procedure YourCallBackProcedure(AEvent: INotifyEvent);
79 | procedure LoadSettingsFromIni;
80 | procedure SaveSettingsToIni;
81 | procedure AutoSubscribe;
82 | end;
83 |
84 | var
85 | ViewMain: TViewMain;
86 |
87 | implementation
88 |
89 | {$R *.dfm}
90 |
91 | uses
92 | System.DateUtils,
93 | Example.Push.Notifications;
94 |
95 | procedure TViewMain.BtnClearTableClick(Sender: TObject);
96 | begin
97 | TableNotification.EmptyDataSet;
98 | end;
99 |
100 | procedure TViewMain.BtnHideClick(Sender: TObject);
101 | begin
102 | Hide;
103 | Tray.Visible := True;
104 | end;
105 |
106 | procedure TViewMain.BtnSubscribeClick(Sender: TObject);
107 | begin
108 | CheckTopics;
109 | CheckSince;
110 | CheckButtons;
111 |
112 | Ntfy
113 | .UserName(LbeUsername.Text)
114 | .Password(LbePassword.Text)
115 | .BaseURL(LbeBaseURL.Text)
116 | .Poll(CkPoll.Checked)
117 | .Since(FSince)
118 | .Scheduled(CkScheduled.Checked);
119 |
120 | Ntfy
121 | .ClearFilters
122 | .Filter(TNotifyFilter.ID, lbeIdFilter.Text)
123 | .Filter(TNotifyFilter.TITLE, lbeFilterTitle.Text)
124 | .Filter(TNotifyFilter.MESSAGECONTENT, lbeFilterMessage.Text)
125 | .Filter(TNotifyFilter.TAGS, lbeFilterTags.Text)
126 | .Filter(TNotifyFilter.PRIORITY, CheckPriority)
127 | .Subscribe(FTopics, YourCallBackProcedure);
128 |
129 | CheckPops;
130 | end;
131 |
132 | procedure TViewMain.BtnUnsubscribeClick(Sender: TObject);
133 | begin
134 | Ntfy.Unsubscribe;
135 | CheckButtons;
136 | CheckPops;
137 | end;
138 |
139 | procedure TViewMain.CheckButtons;
140 | begin
141 | if (not CkPoll.Checked) then
142 | begin
143 | BtnSubscribe.Enabled := not BtnSubscribe.Enabled;
144 | BtnUnsubscribe.Enabled := not BtnUnsubscribe.Enabled;
145 | end;
146 | end;
147 |
148 | procedure TViewMain.CheckPops;
149 | begin
150 | PopSubscribe.Enabled := not PopSubscribe.Enabled;
151 | PopUnsubscribe.Enabled := not PopUnsubscribe.Enabled;
152 | end;
153 |
154 | function TViewMain.CheckPriority: String;
155 | begin
156 | Result := '';
157 | if CbFilterPriority.ItemIndex > 0 then
158 | Result := IntToStr(Ord(TNotifyPriority(CbFilterPriority.ItemIndex)));
159 | end;
160 |
161 | procedure TViewMain.CheckSince;
162 | begin
163 | FSince := '';
164 | if CkSince.Checked then
165 | case GbSince.ItemIndex of
166 | 0, 1: FSince := EdtSince.Text;
167 | 2: FSince := DateTimeToUnix(DtSince.DateTime).ToString;
168 | end;
169 | end;
170 |
171 | procedure TViewMain.CheckTopics;
172 | var
173 | LTopic: String;
174 | begin
175 | FTopics := '';
176 | for LTopic in MemTopics.Lines do
177 | if FTopics = '' then
178 | FTopics := LTopic
179 | else
180 | FTopics := Format('%s,%s', [FTopics, LTopic]);
181 | end;
182 |
183 | procedure TViewMain.CkFiltersExit(Sender: TObject);
184 | begin
185 | if CkScheduled.Checked then
186 | CkPoll.Checked := True;
187 | end;
188 |
189 | procedure TViewMain.CkSinceClick(Sender: TObject);
190 | begin
191 | GbSince.Enabled := not GbSince.Enabled;
192 | end;
193 |
194 | procedure TViewMain.FormClose(Sender: TObject; var Action: TCloseAction);
195 | begin
196 | SaveSettingsToIni;
197 | Ntfy.Unsubscribe;
198 | end;
199 |
200 | procedure TViewMain.FormCreate(Sender: TObject);
201 | begin
202 | FIniFileName := ChangeFileExt(Application.ExeName, '.ini');
203 | LoadSettingsFromIni;
204 |
205 | if CkAutoSubscribe.Checked then
206 | AutoSubscribe;
207 | end;
208 |
209 | procedure TViewMain.LoadSettingsFromIni;
210 | var
211 | Ini: TIniFile;
212 | TopicsList: TStringList;
213 | i: Integer;
214 | begin
215 | if not FileExists(FIniFileName) then
216 | Exit;
217 |
218 | Ini := TIniFile.Create(FIniFileName);
219 | try
220 | // Load general settings
221 | LbeBaseURL.Text := Ini.ReadString('Settings', 'BaseURL', 'https://ntfy.sh');
222 | LbeUsername.Text := Ini.ReadString('Settings', 'Username', '');
223 | LbePassword.Text := Ini.ReadString('Settings', 'Password', '');
224 | CkPoll.Checked := Ini.ReadBool('Settings', 'Poll', True);
225 | CkScheduled.Checked := Ini.ReadBool('Settings', 'Scheduled', False);
226 | CkShowNotification.Checked := Ini.ReadBool('Settings', 'ShowNotification', True);
227 | CkAutoSubscribe.Checked := Ini.ReadBool('Settings', 'AutoSubscribe', False);
228 | CkAutoSaveSubs.Checked := Ini.ReadBool('Settings', 'AutoSaveSubs', False);
229 |
230 | // Load filters
231 | lbeIdFilter.Text := Ini.ReadString('Filters', 'ID', '');
232 | lbeFilterTitle.Text := Ini.ReadString('Filters', 'Title', '');
233 | lbeFilterMessage.Text := Ini.ReadString('Filters', 'Message', '');
234 | lbeFilterTags.Text := Ini.ReadString('Filters', 'Tags', '');
235 | CbFilterPriority.ItemIndex := Ini.ReadInteger('Filters', 'Priority', 0);
236 |
237 | // Load Since settings
238 | CkSince.Checked := Ini.ReadBool('Since', 'Enabled', False);
239 | GbSince.ItemIndex := Ini.ReadInteger('Since', 'Type', 0);
240 | EdtSince.Text := Ini.ReadString('Since', 'Value', '');
241 | DtSince.DateTime := StrToDateTimeDef(Ini.ReadString('Since', 'DateTime', ''), Now);
242 |
243 | // Load topics
244 | MemTopics.Clear;
245 | TopicsList := TStringList.Create;
246 | try
247 | Ini.ReadSection('Topics', TopicsList);
248 | for i := 0 to TopicsList.Count - 1 do
249 | MemTopics.Lines.Add(Ini.ReadString('Topics', TopicsList[i], ''));
250 | finally
251 | TopicsList.Free;
252 | end;
253 |
254 | // Update UI based on loaded settings
255 | GbSince.Enabled := CkSince.Checked;
256 | GbSinceClick(nil);
257 |
258 | finally
259 | Ini.Free;
260 | end;
261 | end;
262 |
263 | procedure TViewMain.SaveSettingsToIni;
264 | var
265 | Ini: TIniFile;
266 | i: Integer;
267 | begin
268 | Ini := TIniFile.Create(FIniFileName);
269 | try
270 | // Save general settings
271 | Ini.WriteString('Settings', 'BaseURL', LbeBaseURL.Text);
272 | Ini.WriteString('Settings', 'Username', LbeUsername.Text);
273 | Ini.WriteString('Settings', 'Password', LbePassword.Text);
274 | Ini.WriteBool('Settings', 'Poll', CkPoll.Checked);
275 | Ini.WriteBool('Settings', 'Scheduled', CkScheduled.Checked);
276 | Ini.WriteBool('Settings', 'ShowNotification', CkShowNotification.Checked);
277 | Ini.WriteBool('Settings', 'AutoSubscribe', CkAutoSubscribe.Checked);
278 | Ini.WriteBool('Settings', 'AutoSaveSubs', CkAutoSaveSubs.Checked);
279 |
280 | // Save filters
281 | Ini.WriteString('Filters', 'ID', lbeIdFilter.Text);
282 | Ini.WriteString('Filters', 'Title', lbeFilterTitle.Text);
283 | Ini.WriteString('Filters', 'Message', lbeFilterMessage.Text);
284 | Ini.WriteString('Filters', 'Tags', lbeFilterTags.Text);
285 | Ini.WriteInteger('Filters', 'Priority', CbFilterPriority.ItemIndex);
286 |
287 | // Save Since settings
288 | Ini.WriteBool('Since', 'Enabled', CkSince.Checked);
289 | Ini.WriteInteger('Since', 'Type', GbSince.ItemIndex);
290 | Ini.WriteString('Since', 'Value', EdtSince.Text);
291 | Ini.WriteString('Since', 'DateTime', DateTimeToStr(DtSince.DateTime));
292 |
293 | // Save topics
294 | Ini.EraseSection('Topics');
295 | for i := 0 to MemTopics.Lines.Count - 1 do
296 | if MemTopics.Lines[i] <> '' then
297 | Ini.WriteString('Topics', 'Topic' + IntToStr(i), MemTopics.Lines[i]);
298 |
299 | finally
300 | Ini.Free;
301 | end;
302 | end;
303 |
304 | procedure TViewMain.AutoSubscribe;
305 | begin
306 | if MemTopics.Lines.Count > 0 then
307 | BtnSubscribe.Click;
308 | end;
309 |
310 | procedure TViewMain.GbSinceClick(Sender: TObject);
311 | begin
312 | case GbSince.ItemIndex of
313 | 0, 1: begin
314 | EdtSince.Enabled := True;
315 | DtSince.Enabled := False;
316 | end;
317 | else
318 | begin
319 | EdtSince.Enabled := False;
320 | DtSince.Enabled := True;
321 | end;
322 | end;
323 | end;
324 |
325 | procedure TViewMain.PopQuitClick(Sender: TObject);
326 | begin
327 | Close;
328 | end;
329 |
330 | procedure TViewMain.PopShowClick(Sender: TObject);
331 | begin
332 | Show;
333 | end;
334 |
335 | procedure TViewMain.PopSubscribeClick(Sender: TObject);
336 | begin
337 | BtnSubscribe.Click;
338 | end;
339 |
340 | procedure TViewMain.PopUnsubscribeClick(Sender: TObject);
341 | begin
342 | BtnUnsubscribe.Click;
343 | end;
344 |
345 | procedure TViewMain.YourCallBackProcedure(AEvent: INotifyEvent);
346 | begin
347 | TableNotification.Open;
348 | TableNotification.AppendRecord([
349 | AEvent.Id,
350 | FormatDateTime('dd/MM/yyyy hh:mm:ss', UnixToDateTime(AEvent.Time)),
351 | IntToStr(Ord(AEvent.Priority)),
352 | AEvent.Title,
353 | AEvent.MessageContent,
354 | AEvent.Topic
355 | ]);
356 |
357 | if CkShowNotification.Checked then
358 | PushWindowsNotification(AEvent);
359 | end;
360 |
361 | end.
362 |
--------------------------------------------------------------------------------
/samples/win-service/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # Windows Service
4 |
5 |
6 |
7 | Under construction
8 |
--------------------------------------------------------------------------------
/src/Notify.Action.Contract.pas:
--------------------------------------------------------------------------------
1 | unit Notify.Action.Contract;
2 |
3 | interface
4 |
5 | uses
6 | Notify.Types,
7 | Notify.Custom.Types,
8 | System.Generics.Collections;
9 |
10 | type
11 | INotifyAction = interface
12 | ['{C7C7E46E-A4BA-440A-8A48-3AD485825186}']
13 | function &Type: TNotifyActionType; overload;
14 | function &Type(const AValue: TNotifyActionType): INotifyAction; overload;
15 | function &Label: String; overload;
16 | function &Label(const AValue: String): INotifyAction; overload;
17 | function Url: String; overload;
18 | function Url(const AValue: String): INotifyAction; overload;
19 | function Clear: Boolean; overload;
20 | function Clear(const AValue: Boolean): INotifyAction; overload;
21 | function Method: String; overload;
22 | function Method(const AValue: String): INotifyAction; overload;
23 | function Body: String; overload;
24 | function Body(const AValue: String): INotifyAction; overload;
25 | function Headers: TNotifyActionHeaders; overload;
26 | function Headers(const AValue: TNotifyActionHeaders): INotifyAction; overload;
27 | function EventHeaders(const AValue: TNotifyActionHeaders): INotifyAction; overload;
28 | function EventHeaders: TNotifyActionHeaders; overload;
29 | function Validate: INotifyAction;
30 | end;
31 |
32 | implementation
33 |
34 | end.
35 |
--------------------------------------------------------------------------------
/src/Notify.Action.DTO.pas:
--------------------------------------------------------------------------------
1 | unit Notify.Action.DTO;
2 |
3 | interface
4 |
5 | uses
6 | Notify.Custom.Types,
7 | Notify.JSON.Parser,
8 | System.Generics.Collections,
9 | REST.Json.Types;
10 |
11 | type
12 |
13 | TNotifyActionDTO = class(TJsonDTO)
14 | private
15 | [JSONName('action')]
16 | FAction: String;
17 | [JSONName('clear')]
18 | FClear: Boolean;
19 | [JSONName('label')]
20 | FLabel: String;
21 | [JSONName('url')]
22 | FUrl: String;
23 | [JSONName('method')]
24 | FMethod: String;
25 | [JSONName('body')]
26 | FBody: String;
27 | [JSONName('headers'), JSONMarshalled]
28 | FHeaders: TNotifyActionHeaders;
29 | published
30 | property Action: String read FAction write FAction;
31 | property Clear: Boolean read FClear write FClear;
32 | property &Label: String read FLabel write FLabel;
33 | property Url: String read FUrl write FUrl;
34 | property Method: String read FMethod write FMethod;
35 | property Body: String read FBody write FBody;
36 | property Headers: TNotifyActionHeaders read FHeaders write FHeaders;
37 | end;
38 |
39 |
40 | implementation
41 |
42 | end.
43 |
--------------------------------------------------------------------------------
/src/Notify.Action.pas:
--------------------------------------------------------------------------------
1 | unit Notify.Action;
2 |
3 | interface
4 |
5 | uses
6 | Notify.Types,
7 | Notify.Custom.Types,
8 | Notify.Action.Contract,
9 | System.Generics.Collections;
10 |
11 | type
12 | TNofifyAction = class sealed(TInterfacedObject, INotifyAction)
13 | private
14 | FType: TNotifyActionType;
15 | FLabel: String;
16 | FUrl: String;
17 | FClear: Boolean;
18 | FMethod: String;
19 | FBody: String;
20 | FEventHeaders: TNotifyActionHeaders;
21 | FHeaders: TNotifyActionHeaders;
22 | public
23 | class function New: INotifyAction;
24 | constructor Create;
25 | destructor Destroy; override;
26 | private
27 | function &Type: TNotifyActionType; overload;
28 | function &Type(const AValue: TNotifyActionType): INotifyAction; overload;
29 | function &Label: String; overload;
30 | function &Label(const AValue: String): INotifyAction; overload;
31 | function Url: String; overload;
32 | function Url(const AValue: String): INotifyAction; overload;
33 | function Clear: Boolean; overload;
34 | function Clear(const AValue: Boolean): INotifyAction; overload;
35 | function Method: String; overload;
36 | function Method(const AValue: String): INotifyAction; overload;
37 | function Body: String; overload;
38 | function Body(const AValue: String): INotifyAction; overload;
39 | function Headers: TNotifyActionHeaders; overload;
40 | function Headers(const AValue: TNotifyActionHeaders): INotifyAction; overload;
41 | function EventHeaders: TNotifyActionHeaders; overload;
42 | function EventHeaders(const AValue: TNotifyActionHeaders): INotifyAction; overload;
43 | function Validate: INotifyAction;
44 | end;
45 |
46 | implementation
47 |
48 | uses
49 | System.SysUtils;
50 |
51 | { TNofifyAction }
52 |
53 | function TNofifyAction.&Type: TNotifyActionType;
54 | begin
55 | Result := FType;
56 | end;
57 |
58 | function TNofifyAction.&Type(const AValue: TNotifyActionType ): INotifyAction;
59 | begin
60 | Result := Self;
61 | FType := AValue;
62 | end;
63 |
64 | function TNofifyAction.Body: String;
65 | begin
66 | Result := FBody;
67 | end;
68 |
69 | function TNofifyAction.Body(const AValue: String): INotifyAction;
70 | begin
71 | Result := Self;
72 | FBody := AValue;
73 | end;
74 |
75 | function TNofifyAction.Clear(const AValue: Boolean): INotifyAction;
76 | begin
77 | Result := Self;
78 | FClear := AValue;
79 | end;
80 |
81 | constructor TNofifyAction.Create;
82 | begin
83 | FMethod := 'POST';
84 | FType := TNotifyActionType.VIEW;
85 | end;
86 |
87 | destructor TNofifyAction.Destroy;
88 | begin
89 | FEventHeaders.Free;
90 | inherited;
91 | end;
92 |
93 | function TNofifyAction.EventHeaders(const AValue: TNotifyActionHeaders): INotifyAction;
94 | begin
95 | Result := Self;
96 | FEventHeaders := TNotifyActionHeaders(AValue);
97 | end;
98 |
99 | function TNofifyAction.EventHeaders: TNotifyActionHeaders;
100 | begin
101 | Result := FEventHeaders;
102 | end;
103 |
104 | function TNofifyAction.Headers: TNotifyActionHeaders;
105 | begin
106 | Result := FHeaders;
107 | end;
108 |
109 | function TNofifyAction.Clear: Boolean;
110 | begin
111 | Result := FClear;
112 | end;
113 |
114 | function TNofifyAction.&Label(const AValue: String): INotifyAction;
115 | begin
116 | Result := Self;
117 | FLabel := AValue;
118 | end;
119 |
120 | function TNofifyAction.Method: String;
121 | begin
122 | Result := FMethod;
123 | end;
124 |
125 | function TNofifyAction.Method(const AValue: String): INotifyAction;
126 | begin
127 | Result := Self;
128 | FMethod := AValue;
129 | end;
130 |
131 | function TNofifyAction.&Label: String;
132 | begin
133 | Result := FLabel;
134 | end;
135 |
136 | class function TNofifyAction.New: INotifyAction;
137 | begin
138 | Result := Self.Create;
139 | end;
140 |
141 | function TNofifyAction.Url: String;
142 | begin
143 | Result := FUrl;
144 | end;
145 |
146 | function TNofifyAction.Url(const AValue: String): INotifyAction;
147 | begin
148 | Result := Self;
149 | FUrl := AValue;
150 | end;
151 |
152 | function TNofifyAction.Validate: INotifyAction;
153 | begin
154 | if FUrl = '' then
155 | raise Exception.Create('URL field is missing in one of the action items');
156 |
157 | if FLabel = '' then
158 | raise Exception.Create('Label field is missing in one of the action items');
159 | end;
160 |
161 | function TNofifyAction.Headers(const AValue: TNotifyActionHeaders): INotifyAction;
162 | begin
163 | Result := Self;
164 | FHeaders := AValue;
165 | end;
166 |
167 | end.
168 |
--------------------------------------------------------------------------------
/src/Notify.Api.Contract.pas:
--------------------------------------------------------------------------------
1 | unit Notify.Api.Contract;
2 |
3 | interface
4 |
5 | uses
6 | System.Classes,
7 | Notify.Config.Contract,
8 | Notify.Api.Response;
9 |
10 | type
11 | INotifyApi = interface
12 | ['{4A4C86DB-6176-404E-A317-BA789ED4848B}']
13 | function Response: TNotifyApiResponse;
14 | function Get: INotifyApi;
15 | function Post: INotifyApi;
16 | function Put: INotifyApi;
17 | function ClearHeaders: INotifyApi;
18 | function ClearBody: INotifyApi;
19 | function ClearURLParameters: INotifyApi;
20 | function ClearEndPoint: INotifyApi;
21 | function AddHeader(const PName: String; AValue: String): INotifyApi; overload;
22 | function AddHeader(const AName: String; AValues: array of String): INotifyApi; overload;
23 | function AddBody(const AValue: String): INotifyApi; overload;
24 | function AddBody(const AValue: TFileStream): INotifyApi; overload;
25 | function AddEndPoint(const AValue: String): INotifyApi; overload;
26 | function AddURLParameter(const AName: String; AValue: String): INotifyApi;
27 | function Config(const AValue: INotifyConfig): INotifyApi;
28 | function AbortStream: INotifyApi;
29 | function Disconnect: INotifyApi;
30 | end;
31 |
32 | INotifyApiFactory = interface
33 | ['{66E16F57-69CD-41CC-9457-866A4E5D396E}']
34 | function Api: INotifyApi;
35 | end;
36 |
37 | implementation
38 |
39 | end.
40 |
--------------------------------------------------------------------------------
/src/Notify.Api.Factory.pas:
--------------------------------------------------------------------------------
1 | unit Notify.Api.Factory;
2 |
3 | interface
4 |
5 | uses
6 | Notify.Api.Contract;
7 |
8 | type
9 | TNotifyApiFactory = class(TInterfacedObject, INotifyApiFactory)
10 | public
11 | class function New: INotifyApiFactory;
12 | function Api: INotifyApi;
13 | end;
14 |
15 | implementation
16 |
17 | uses
18 | Notify.Api.NetHTTP,
19 | Notify.Api.Indy;
20 |
21 | { TNotifyApiFactory }
22 |
23 | function TNotifyApiFactory.Api: INotifyApi;
24 | begin
25 | {$IFDEF NTFY_HTTP_INDY}
26 | Result := TNotifyApiIndy.New
27 | {$ELSE}
28 | Result := TNotifyApiNetHTTP.New;
29 | {$ENDIF}
30 | end;
31 |
32 | class function TNotifyApiFactory.New: INotifyApiFactory;
33 | begin
34 | Result := Self.Create;
35 | end;
36 |
37 | end.
38 |
--------------------------------------------------------------------------------
/src/Notify.Api.Indy.pas:
--------------------------------------------------------------------------------
1 | unit Notify.Api.Indy;
2 |
3 | interface
4 |
5 | uses
6 | System.Classes,
7 | System.Threading,
8 | IdBaseComponent,
9 | IdComponent,
10 | IdIOHandler,
11 | IdIOHandlerSocket,
12 | IdIOHandlerStack,
13 | IdSSL,
14 | IdSSLOpenSSL,
15 | IdTCPConnection,
16 | IdTCPClient,
17 | IdHTTP,
18 | IdStream,
19 | IdGlobal,
20 | Notify.Api.Contract,
21 | Notify.Config.Contract,
22 | Notify.Subscription.Event,
23 | NX.Horizon,
24 | Notify.Api.Response;
25 |
26 | type
27 |
28 | TSSEThread = class(TThread)
29 | private
30 | FUrl: String;
31 | FIdHttp: TIdHTTP;
32 | FIdEventStream: TMemoryStream;
33 | FResponse: TNotifyApiResponse;
34 | FConfig: INotifyConfig;
35 | const FCloseConnectionMessage = '/\/\';
36 | procedure DoOnWork(ASender: TObject; AWorkMode: TWorkMode; AWorkCount: Int64);
37 | public
38 | constructor Create(AUrl: String; var AIdHttp: TIdHTTP; var AResponse: TNotifyApiResponse; var AConfig: INotifyConfig);
39 | procedure Execute; override;
40 | destructor Destroy; override;
41 | procedure AbortStream;
42 | end;
43 |
44 | TNotifyApiIndy = class(TInterfacedObject, INotifyApi)
45 | strict private
46 | FIOHandlerSSL: TIdSSLIOHandlerSocketOpenSSL;
47 | FIdHTTP: TIdHTTP;
48 | FBodyStream: TMemoryStream;
49 | FNotifyConfig: INotifyConfig;
50 | FEndPoint: String;
51 | FConnectionThread: TSSEThread;
52 | FURLParametersList: TStringList;
53 | public
54 | constructor Create;
55 | destructor Destroy; override;
56 | class function New: INotifyApi;
57 | private
58 | FResponse: TNotifyApiResponse;
59 | function Config(const AValue: INotifyConfig): INotifyApi;
60 | function ClearHeaders: INotifyApi;
61 | function ClearBody: INotifyApi;
62 | function ClearURLParameters: INotifyApi;
63 | function ClearEndPoint: INotifyApi;
64 | function AddHeader(const AName: String; AValue: String): INotifyApi; overload;
65 | function AddHeader(const AName: String; AValues: array of String): INotifyApi; overload;
66 | function AddBody(const AValue: String): INotifyApi; overload;
67 | function AddBody(const AValue: TFileStream): INotifyApi; overload;
68 | function AddEndPoint(const AValue: String): INotifyApi; overload;
69 | function AddURLParameter(const AName: String; AValue: String): INotifyApi;
70 | function Get: INotifyApi;
71 | function Post: INotifyApi;
72 | function Put: INotifyApi;
73 | function AbortStream: INotifyApi;
74 | function CreateURL: String;
75 | function Response: TNotifyApiResponse;
76 | procedure LogRequest;
77 | function Disconnect: INotifyApi;
78 | end;
79 |
80 | implementation
81 |
82 | uses
83 | IdURI,
84 | Notify.SmartPointer,
85 | System.SysUtils,
86 | System.StrUtils,
87 | System.Types,
88 | Notify.Logs,
89 | Notify.Notification.DTO,
90 | Notify.Error,
91 | Notify.Response.Data;
92 |
93 | { TNotityApiIndy }
94 |
95 | function TNotifyApiIndy.AddBody(const AValue: String): INotifyApi;
96 | var
97 | LBodyStream: TSmartPointer;
98 | begin
99 | Result := Self;
100 | LBodyStream := TStringStream.Create(AValue);
101 | FBodyStream.CopyFrom(LBodyStream.Value, LBodyStream.Value.Size);
102 | end;
103 |
104 | function TNotifyApiIndy.AbortStream: INotifyApi;
105 | begin
106 | if Assigned(FConnectionThread) then
107 | begin
108 | try
109 | if not FConnectionThread.Terminated then
110 | begin
111 | FConnectionThread.AbortStream;
112 | FConnectionThread.Terminate;
113 | FConnectionThread.WaitFor;
114 | end;
115 | finally
116 | FConnectionThread.Free;
117 | FConnectionThread := nil;
118 | end;
119 | end;
120 | end;
121 |
122 | function TNotifyApiIndy.AddBody(const AValue: TFileStream): INotifyApi;
123 | begin
124 | Result := Self;
125 | FBodyStream.CopyFrom(AValue, AValue.Size);
126 | end;
127 |
128 | function TNotifyApiIndy.AddEndPoint(const AValue: String): INotifyApi;
129 | begin
130 | Result := Self;
131 | FEndPoint := AValue;
132 | end;
133 |
134 | function TNotifyApiIndy.AddHeader(const AName: String; AValues: array of String): INotifyApi;
135 | var
136 | LString, LValue: String;
137 | begin
138 | Result := Self;
139 | for LString in AValues do
140 | if LValue = '' then
141 | LValue := LString
142 | else
143 | LValue := Format('%s, %s', [LValue, LString]);
144 | if LValue <> '' then
145 | begin
146 | FIdHTTP.Request.CustomHeaders.AddValue(AName, LValue);
147 | end;
148 | end;
149 |
150 | function TNotifyApiIndy.AddURLParameter(const AName: String; AValue: String): INotifyApi;
151 | var
152 | LParam: String;
153 | begin
154 | Result := Self;
155 | LParam := Format('%s=%s', [AName, AValue]);
156 | FURLParametersList.Add(LParam);
157 | end;
158 |
159 | function TNotifyApiIndy.AddHeader(const AName: String; AValue: String): INotifyApi;
160 | begin
161 | Result := Self;
162 | FIdHTTP.Request.CustomHeaders.AddValue(AName, AValue);
163 | end;
164 |
165 | function TNotifyApiIndy.ClearBody: INotifyApi;
166 | begin
167 | Result := Self;
168 | FBodyStream.Clear;
169 | end;
170 |
171 | function TNotifyApiIndy.ClearEndPoint: INotifyApi;
172 | begin
173 | Result := Self;
174 | FEndPoint := '';
175 | end;
176 |
177 | function TNotifyApiIndy.ClearHeaders: INotifyApi;
178 | begin
179 | Result := Self;
180 | FIdHTTP.Request.CustomHeaders.Clear;
181 | end;
182 |
183 | function TNotifyApiIndy.ClearURLParameters: INotifyApi;
184 | begin
185 | Result := Self;
186 | FURLParametersList.Clear;
187 | end;
188 |
189 | function TNotifyApiIndy.Config(const AValue: INotifyConfig): INotifyApi;
190 | begin
191 | Result := Self;
192 | FNotifyConfig := AValue;
193 |
194 | if AValue.ProxyServer <> '' then
195 | begin
196 | FIdHttp.ProxyParams.ProxyServer := AValue.ProxyServer;
197 | FIdHttp.ProxyParams.ProxyUsername := AValue.ProxyUser;
198 | FIdHttp.ProxyParams.ProxyPassword := AValue.ProxyPassword;
199 | FIdHttp.ProxyParams.ProxyPort := AValue.ProxyPort;
200 | end;
201 |
202 | end;
203 |
204 | constructor TNotifyApiIndy.Create;
205 | begin
206 | FIdHTTP := TIdHTTP.Create(nil);
207 | FIOHandlerSSL := TIdSSLIOHandlerSocketOpenSSL.Create;
208 | FBodyStream := TMemoryStream.Create;
209 | FIOHandlerSSL.SSLOptions.Method := sslvTLSv1_2;
210 | FIdHTTP.IOHandler := FIOHandlerSSL;
211 | FIdHTTP.HTTPOptions := [hoNoReadMultipartMIME];
212 | FURLParametersList := TStringList.Create;
213 | FResponse := TNotifyApiResponse.Create;
214 | end;
215 |
216 | function TNotifyApiIndy.CreateURL: String;
217 | var
218 | I: Integer;
219 | begin
220 |
221 | if FEndPoint <> '' then
222 | Result := Format('%s/%s', [FNotifyConfig.BaseURL, FEndPoint])
223 | else
224 | Result := FNotifyConfig.BaseURL;
225 |
226 | if FURLParametersList.Count > 0 then
227 | begin
228 | Result := Result + '?';
229 | for I := 0 to Pred(FURLParametersList.Count) do
230 | begin
231 | if I > 0 then
232 | Result := Result + '&';
233 | Result := Result + FURLParametersList.Strings[I];
234 | end;
235 | end;
236 | end;
237 |
238 | destructor TNotifyApiIndy.Destroy;
239 | begin
240 | try
241 | AbortStream;
242 | finally
243 | FResponse.Free;
244 | FIdHTTP.Free;
245 | FIOHandlerSSL.Free;
246 | FURLParametersList.Free;
247 | FBodyStream.Free;
248 | end;
249 | inherited;
250 | end;
251 |
252 | function TNotifyApiIndy.Disconnect: INotifyApi;
253 | begin
254 | Result := Self;
255 | FIdHTTP.Disconnect;
256 | end;
257 |
258 | function TNotifyApiIndy.Get: INotifyApi;
259 | begin
260 | Result := Self;
261 | try
262 | if Assigned(FConnectionThread) then
263 | AbortStream;
264 | finally
265 | FConnectionThread := TSSEThread.Create(TIdURI.URLEncode(CreateURL), FIdHTTP, FResponse, FNotifyConfig);
266 | FConnectionThread.Start;
267 | end;
268 |
269 | {$IFDEF CONSOLE OR CONSOLE_TESTRUNNER}
270 | FConnectionThread.WaitFor;
271 | {$ENDIF}
272 |
273 | end;
274 |
275 | procedure TNotifyApiIndy.LogRequest;
276 | begin
277 | if FNotifyConfig.SaveLog then
278 | begin
279 | TNotifyLogs.Log(FNotifyConfig.LogPath, FIdHTTP.URL.GetFullURI());
280 | TNotifyLogs.Log(FNotifyConfig.LogPath, FBodyStream);
281 | TNotifyLogs.Log(FNotifyConfig.LogPath, FResponse.StatusCode.ToString);
282 | TNotifyLogs.Log(FNotifyConfig.LogPath, FResponse.ResponseStream);
283 | end;
284 | end;
285 |
286 | class function TNotifyApiIndy.New: INotifyApi;
287 | begin
288 | Result := Self.Create;
289 | end;
290 |
291 | function TNotifyApiIndy.Post: INotifyApi;
292 | begin
293 | Result := Self;
294 | try
295 | FIdHTTP.Post(TIdURI.URLEncode(CreateURL), FBodyStream, FResponse.ResponseStream);
296 | finally
297 | FResponse.StatusCode := FIdHttp.ResponseCode;
298 | LogRequest;
299 | end;
300 | end;
301 |
302 | function TNotifyApiIndy.Put: INotifyApi;
303 | begin
304 | Result := Self;
305 | try
306 | FIdHTTP.Put(TIdURI.URLEncode(CreateURL), FBodyStream, FResponse.ResponseStream);
307 | finally
308 | FResponse.StatusCode := FIdHttp.ResponseCode;
309 | LogRequest;
310 | end;
311 | end;
312 |
313 | function TNotifyApiIndy.Response: TNotifyApiResponse;
314 | begin
315 | Result := FResponse;
316 | end;
317 |
318 | { TSSEThread }
319 |
320 | procedure TSSEThread.AbortStream;
321 | var
322 | Cmd: String;
323 | begin
324 | try
325 | Cmd := FCloseConnectionMessage;
326 | FIdEventStream.WriteData(ToBytes(Cmd), Length(Cmd));
327 | FIdEventStream.Position := 0;
328 | DoOnWork(Self, wmRead, 1000);
329 | except on E: Exception do
330 | begin
331 | Exit;
332 | end;
333 | end;
334 | end;
335 |
336 | constructor TSSEThread.Create(AUrl: String; var AIdHttp: TIdHTTP; var AResponse: TNotifyApiResponse; var AConfig: INotifyConfig);
337 | begin
338 | inherited Create(True);
339 | FreeOnTerminate := False;
340 | FUrl := AUrl;
341 | FIdHttp := AIdHttp;
342 | FResponse := AResponse;
343 | FConfig := AConfig;
344 | end;
345 |
346 | destructor TSSEThread.Destroy;
347 | begin
348 | inherited;
349 | end;
350 |
351 | procedure TSSEThread.DoOnWork(ASender: TObject; AWorkMode: TWorkMode; AWorkCount: Int64);
352 | var
353 | LEventString: UTF8String;
354 | LString: String;
355 | {$IFDEF VER310}
356 | LStrings: TStringDynArray;
357 | {$ELSE}
358 | LStrings: TArray;
359 | {$ENDIF}
360 | begin
361 |
362 | if Terminated then
363 | Exit;
364 |
365 | FIdEventStream.Position := 0;
366 | SetString(LEventString, PAnsiChar(FIdEventStream.Memory), FIdEventStream.Size);
367 |
368 | if LEventString = '' then
369 | Exit;
370 |
371 | if LEventString = FCloseConnectionMessage then
372 | FIdHttp.Socket.Close;
373 |
374 | LStrings := SplitString(UTF8ToString(LEventString), #$A);
375 |
376 | for LString in LStrings do
377 | {$IF DEFINED(ANDROID)}
378 | NxHorizon.Instance.Send(Utf8String(LString), Sync);
379 | {$ELSE}
380 | NxHorizon.Instance.Post(Utf8String(LString));
381 | {$ENDIF}
382 |
383 | end;
384 |
385 | procedure TSSEThread.Execute;
386 | begin
387 | inherited;
388 | FIdEventStream := TMemoryStream.Create;
389 | try
390 | FIdHttp.OnWork := DoOnWork;
391 | FIdHttp.Request.CacheControl := 'no-cache';
392 | FIdHttp.Request.Accept := 'text/event-stream';
393 | FIdHttp.Response.KeepAlive := True;
394 |
395 | while not Terminated do
396 | begin
397 | try
398 | try
399 | FIdHttp.Get(FUrl, FIdEventStream);
400 | {$IF DEFINED(CONSOLE)}
401 | if FConfig.SaveLog then
402 | WriteLn(DateTimeToStr(Now) + ' Exiting Get HTTP Call');
403 | {$IFEND}
404 |
405 | if not FConfig.Poll then
406 | TThread.Sleep(10000);
407 |
408 | finally
409 | if FConfig.Poll then
410 | Terminate;
411 | end;
412 | except on E: Exception do
413 | begin
414 | if FConfig.Poll then
415 | Exit;
416 | end;
417 | end;
418 | end;
419 |
420 | finally
421 | FIdEventStream.Free;
422 | end;
423 | end;
424 |
425 | end.
426 |
--------------------------------------------------------------------------------
/src/Notify.Api.Response.pas:
--------------------------------------------------------------------------------
1 | unit Notify.Api.Response;
2 |
3 | interface
4 |
5 | ///
6 | /// Response is only used for publishing
7 | ///
8 |
9 | uses
10 | Notify.Error,
11 | Notify.Response.Data,
12 | Notify.Notification.DTO,
13 | System.Classes;
14 |
15 | type
16 |
17 | {$M+}
18 |
19 | TNotifyApiResponse = class
20 | private
21 | {$IFDEF WIN32}
22 | FStatusCode: Integer;
23 | {$ELSE}
24 | FStatusCode: Int64;
25 | {$ENDIF}
26 | FResponseStream: TMemoryStream;
27 | FResponseData: TNotifyResponseDTO;
28 | FResponseErrors: TNotifyErrors;
29 | function GetErros: TNotifyErrors;
30 | function GetData: TNotifyResponseDTO;
31 | public
32 | constructor Create;
33 | destructor Destroy; override;
34 | published
35 | property Erros: TNotifyErrors read GetErros;
36 | property Data: TNotifyResponseDTO read GetData;
37 | property {$IFDEF WIN32} StatusCode: Integer {$ELSE} StatusCode: Int64 {$ENDIF} read FStatusCode write FStatusCode;
38 | property ResponseStream: TMemoryStream read FResponseStream write FResponseStream;
39 | end;
40 |
41 | implementation
42 |
43 | uses
44 | System.SysUtils;
45 |
46 | { TNotifyApiResponse }
47 |
48 | constructor TNotifyApiResponse.Create;
49 | begin
50 | FResponseData := TNotifyResponseDTO.Create;
51 | FResponseErrors := TNotifyErrors.Create;
52 | FResponseStream := TMemoryStream.Create
53 | end;
54 |
55 | destructor TNotifyApiResponse.Destroy;
56 | begin
57 | FreeAndNil(FResponseData);
58 | FreeAndNil(FResponseErrors);
59 | FreeAndNil(FResponseStream);
60 | inherited
61 | end;
62 |
63 | function TNotifyApiResponse.GetData: TNotifyResponseDTO;
64 | var
65 | LRawString: String;
66 | begin
67 | Result := FResponseData;
68 |
69 | if not Assigned(FResponseData) then
70 | Exit;
71 |
72 | FResponseStream.Position := 0;
73 | SetString(LRawString, PAnsiChar(FResponseStream.Memory), FResponseStream.Size);
74 | FResponseData.AsJson := LRawString;
75 | end;
76 |
77 | function TNotifyApiResponse.GetErros: TNotifyErrors;
78 | var
79 | LRawString: String;
80 | begin
81 | Result := FResponseErrors;
82 |
83 | if not Assigned(FResponseData) then
84 | Exit;
85 |
86 | FResponseStream.Position := 0;
87 | SetString(LRawString, PAnsiChar(FResponseStream.Memory), FResponseStream.Size);
88 | FResponseErrors.AsJson := LRawString;
89 | end;
90 |
91 | end.
92 |
--------------------------------------------------------------------------------
/src/Notify.Attachment.Contract.pas:
--------------------------------------------------------------------------------
1 | unit Notify.Attachment.Contract;
2 |
3 | interface
4 |
5 | type
6 | INotifyAttachment = interface
7 | ['{2F96DFC0-08A4-4096-BF48-578E4B6EB327}']
8 | function Name: String; overload;
9 | function Name(const AValue: String): INotifyAttachment; overload;
10 | function Url: String; overload;
11 | function Url(const AValue:String): INotifyAttachment; overload;
12 | function MimeType: String; overload;
13 | function MimeType(const AValue: String): INotifyAttachment; overload;
14 | function Size: Integer; overload;
15 | function Size(const AValue: Integer): INotifyAttachment; overload;
16 | function Expires: Integer; overload;
17 | function Expires(const AValue: Integer): INotifyAttachment; overload;
18 | end;
19 |
20 | implementation
21 |
22 | end.
23 |
--------------------------------------------------------------------------------
/src/Notify.Attachment.DTO.pas:
--------------------------------------------------------------------------------
1 | unit Notify.Attachment.DTO;
2 |
3 | interface
4 |
5 | uses
6 | Rest.Json.Types,
7 | Notify.JSON.Parser;
8 |
9 | type
10 | TNotifyAttachmentDTO = class(TJsonDTO)
11 | private
12 | [JSONName('name')]
13 | FName: String;
14 | [JSONName('url')]
15 | FUrl: String;
16 | [JSONName('type')]
17 | FMimeType: String;
18 | [JSONName('size')]
19 | FSize: Integer;
20 | [JSONName('expires')]
21 | FExpires: Integer;
22 | published
23 | property Name: String read FName write FName;
24 | property Url: String read FUrl write FUrl;
25 | property MimeType: String read FMimeType write FMimeType;
26 | property Size: Integer read FSize write FSize;
27 | property Expires: Integer read FExpires write FExpires;
28 | end;
29 |
30 |
31 | implementation
32 |
33 | end.
34 |
--------------------------------------------------------------------------------
/src/Notify.Attachment.pas:
--------------------------------------------------------------------------------
1 | unit Notify.Attachment;
2 |
3 | interface
4 |
5 | uses
6 | Notify.Attachment.Contract;
7 |
8 | type
9 | TNotifyAttachment = class(TInterfacedObject, INotifyAttachment)
10 | strict private
11 | FName: String;
12 | FUrl: String;
13 | FMimeType: String;
14 | FSize: Integer;
15 | FExpires: Integer;
16 | public
17 | class function New: INotifyAttachment;
18 | private
19 | function Name: String; overload;
20 | function Name(const AValue: String): INotifyAttachment; overload;
21 | function Url: String; overload;
22 | function Url(const AValue:String): INotifyAttachment; overload;
23 | function MimeType: String; overload;
24 | function MimeType(const AValue: String): INotifyAttachment; overload;
25 | function Size: Integer; overload;
26 | function Size(const AValue: Integer): INotifyAttachment; overload;
27 | function Expires: Integer; overload;
28 | function Expires(const AValue: Integer): INotifyAttachment; overload;
29 | end;
30 |
31 | implementation
32 |
33 | { TNotifyAttachment }
34 |
35 | function TNotifyAttachment.Expires: Integer;
36 | begin
37 | Result := FExpires;
38 | end;
39 |
40 | function TNotifyAttachment.Expires(const AValue: Integer): INotifyAttachment;
41 | begin
42 | Result := Self;
43 | FExpires := AValue;
44 | end;
45 |
46 | function TNotifyAttachment.MimeType: String;
47 | begin
48 | Result := FMimeType;
49 | end;
50 |
51 | function TNotifyAttachment.MimeType(const AValue: String): INotifyAttachment;
52 | begin
53 | Result := Self;
54 | FMimeType := AValue;
55 | end;
56 |
57 | function TNotifyAttachment.Name(const AValue: String): INotifyAttachment;
58 | begin
59 | Result := Self;
60 | FName := AValue;
61 | end;
62 |
63 | function TNotifyAttachment.Name: String;
64 | begin
65 | Result := FName;
66 | end;
67 |
68 | class function TNotifyAttachment.New: INotifyAttachment;
69 | begin
70 | Result := Self.Create;
71 | end;
72 |
73 | function TNotifyAttachment.Size: Integer;
74 | begin
75 | Result := FSize;
76 | end;
77 |
78 | function TNotifyAttachment.Size(const AValue: Integer): INotifyAttachment;
79 | begin
80 | Result := Self;
81 | FSize := AValue;
82 | end;
83 |
84 | function TNotifyAttachment.Url: String;
85 | begin
86 | Result := FUrl;
87 | end;
88 |
89 | function TNotifyAttachment.Url(const AValue: String): INotifyAttachment;
90 | begin
91 | Result := Self;
92 | FUrl := AValue;
93 | end;
94 |
95 | end.
96 |
--------------------------------------------------------------------------------
/src/Notify.Config.Contract.pas:
--------------------------------------------------------------------------------
1 | unit Notify.Config.Contract;
2 |
3 | interface
4 |
5 | uses
6 | Notify.Types;
7 |
8 | type
9 | INotifyConfig = interface
10 | ['{8CFCA0D0-3637-4367-9F56-B420D5441659}']
11 | function BaseURL: String; overload;
12 | function BaseURL(const AValue: String): INotifyConfig; overload;
13 | function UserName: String; overload;
14 | function UserName(const AValue: String): INotifyConfig; overload;
15 | function Password: String; overload;
16 | function Password(const AValue: String): INotifyConfig; overload;
17 | function Cache: Boolean; overload;
18 | function Cache(const AValue: Boolean): INotifyConfig; overload;
19 | function DisableFireBase: Boolean; overload;
20 | function DisableFireBase(const AValue: Boolean): INotifyConfig; overload;
21 | function SaveLog: Boolean; overload;
22 | function SaveLog(const AValue: Boolean): INotifyConfig; overload;
23 | function LogPath: String; overload;
24 | function LogPath(const AValue: String): INotifyConfig; overload;
25 | function SubscriptionType: TNotifySubscriptionType; overload;
26 | function SubscriptionType(const AValue: TNotifySubscriptionType): INotifyConfig; overload;
27 | function ProxyServer: string; overload;
28 | function ProxyUser: string; overload;
29 | function ProxyPassword: string; overload;
30 | function ProxyPort: integer; overload;
31 | function Proxy(const aProxyServer, aProxyUser, aProxyPassword: string; const aProxyPort: integer): INotifyConfig; overload;
32 | function Poll: Boolean; overload;
33 | function Poll(const AValue: Boolean): INotifyConfig; overload;
34 | function Since: String; overload;
35 | function Since(const AValue: String): INotifyConfig; overload;
36 | function Scheduled: Boolean; overload;
37 | function Scheduled(const AValue: Boolean): INotifyConfig; overload;
38 | end;
39 |
40 | implementation
41 |
42 | end.
43 |
--------------------------------------------------------------------------------
/src/Notify.Config.pas:
--------------------------------------------------------------------------------
1 | unit Notify.Config;
2 |
3 | interface
4 |
5 | uses
6 | Notify.Types,
7 | Notify.Config.Contract;
8 |
9 | type
10 | TNotifyConfig = class sealed(TInterfacedObject, INotifyConfig)
11 | strict private
12 | FBaseURL: String;
13 | FUserName: String;
14 | FPassword: String;
15 | FCache: Boolean;
16 | FDisableFirebaseFCM: Boolean;
17 | FSaveLog: Boolean;
18 | FLogPath: String;
19 | FSubscriptionType: TNotifySubscriptionType;
20 | FProxyServer, FProxyUser, FProxyPassword: string;
21 | FProxyPort: integer;
22 | FPoll: Boolean;
23 | FSince: String;
24 | FScheduled: Boolean;
25 | public
26 | class function New: INotifyConfig;
27 | private
28 | constructor Create;
29 | function BaseURL: String; overload;
30 | function BaseURL(const AValue: String): INotifyConfig; overload;
31 | function UserName: String; overload;
32 | function UserName(const AValue: String): INotifyConfig; overload;
33 | function Password: String; overload;
34 | function Password(const AValue: String): INotifyConfig; overload;
35 | function Cache: Boolean; overload;
36 | function Cache(const AValue: Boolean): INotifyConfig; overload;
37 | function DisableFireBase: Boolean; overload;
38 | function DisableFireBase(const AValue: Boolean): INotifyConfig; overload;
39 | function SaveLog: Boolean; overload;
40 | function SaveLog(const AValue: Boolean): INotifyConfig; overload;
41 | function LogPath: String; overload;
42 | function LogPath(const AValue: String): INotifyConfig; overload;
43 | function SubscriptionType: TNotifySubscriptionType; overload;
44 | function SubscriptionType(const AValue: TNotifySubscriptionType): INotifyConfig; overload;
45 | function ProxyServer: string; overload;
46 | function ProxyUser: string; overload;
47 | function ProxyPassword: string; overload;
48 | function ProxyPort: integer; overload;
49 | function Proxy(const aProxyServer, aProxyUser, aProxyPassword: string; const aProxyPort: integer): INotifyConfig; overload;
50 | function Poll: Boolean; overload;
51 | function Poll(const AValue: Boolean): INotifyConfig; overload;
52 | function Since: String; overload;
53 | function Since(const AValue: String): INotifyConfig; overload;
54 | function Scheduled: Boolean; overload;
55 | function Scheduled(const AValue: Boolean): INotifyConfig; overload;
56 | end;
57 |
58 | implementation
59 |
60 | uses
61 | System.SysUtils;
62 |
63 | { TNotifyConfig }
64 |
65 | function TNotifyConfig.BaseURL(const AValue: String): INotifyConfig;
66 | begin
67 | Result := Self;
68 | FBaseURL := AValue;
69 | end;
70 |
71 | function TNotifyConfig.Cache(const AValue: Boolean): INotifyConfig;
72 | begin
73 | Result := Self;
74 | FCache := AValue;
75 | end;
76 |
77 | constructor TNotifyConfig.Create;
78 | begin
79 | FBaseURL := 'https://ntfy.sh';
80 | FCache := True;
81 | FLogPath := ExtractFilePath(ParamStr(0));
82 | FSubscriptionType := TNotifySubscriptionType.JSON;
83 | end;
84 |
85 | function TNotifyConfig.DisableFireBase(const AValue: Boolean): INotifyConfig;
86 | begin
87 | Result := Self;
88 | FDisableFirebaseFCM := AValue;
89 | end;
90 |
91 | function TNotifyConfig.LogPath(const AValue: String): INotifyConfig;
92 | begin
93 | Result := Self;
94 | FLogPath := AValue;
95 | end;
96 |
97 | function TNotifyConfig.LogPath: String;
98 | begin
99 | Result := FLogPath;
100 | end;
101 |
102 | function TNotifyConfig.DisableFireBase: Boolean;
103 | begin
104 | Result := FDisableFirebaseFCM;
105 | end;
106 |
107 | function TNotifyConfig.Cache: Boolean;
108 | begin
109 | Result := FCache;
110 | end;
111 |
112 | function TNotifyConfig.BaseURL: String;
113 | begin
114 | Result := FBaseURL;
115 | end;
116 |
117 | class function TNotifyConfig.New: INotifyConfig;
118 | begin
119 | Result := Self.Create;
120 | end;
121 |
122 | function TNotifyConfig.Password: String;
123 | begin
124 | Result := FPassword;
125 | end;
126 |
127 | function TNotifyConfig.Password(const AValue: String): INotifyConfig;
128 | begin
129 | Result := Self;
130 | FPassword := AValue;
131 | end;
132 |
133 | function TNotifyConfig.Poll: Boolean;
134 | begin
135 | Result := FPoll;
136 | end;
137 |
138 | function TNotifyConfig.Poll(const AValue: Boolean): INotifyConfig;
139 | begin
140 | Result := Self;
141 | FPoll := AValue;
142 | end;
143 |
144 | function TNotifyConfig.Proxy(const AProxyServer, AProxyUser, AProxyPassword: string; const aProxyPort: integer): INotifyConfig;
145 | begin
146 | FProxyServer := AProxyServer;
147 | FProxyUser := AProxyUser;
148 | FProxyPassword := AProxyPassword;
149 | FProxyPort := AProxyPort;
150 | end;
151 |
152 | function TNotifyConfig.ProxyPassword: string;
153 | begin
154 | Result := FProxyPassword;
155 | end;
156 |
157 | function TNotifyConfig.ProxyPort: integer;
158 | begin
159 | Result := FProxyPort;
160 | end;
161 |
162 | function TNotifyConfig.ProxyServer: string;
163 | begin
164 | Result := FProxyServer;
165 | end;
166 |
167 | function TNotifyConfig.ProxyUser: string;
168 | begin
169 | Result := FProxyUser;
170 | end;
171 |
172 | function TNotifyConfig.SaveLog(const AValue: Boolean): INotifyConfig;
173 | begin
174 | Result := Self;
175 | FSaveLog := AValue;
176 | end;
177 |
178 | function TNotifyConfig.Scheduled(const AValue: Boolean): INotifyConfig;
179 | begin
180 | Result := Self;
181 | FScheduled := AValue;
182 | end;
183 |
184 | function TNotifyConfig.Scheduled: Boolean;
185 | begin
186 | Result := FScheduled;
187 | end;
188 |
189 | function TNotifyConfig.Since: String;
190 | begin
191 | Result := FSince;
192 | end;
193 |
194 | function TNotifyConfig.Since(const AValue: String): INotifyConfig;
195 | begin
196 | Result := Self;
197 | FSince := AValue;
198 | end;
199 |
200 | function TNotifyConfig.SubscriptionType(const AValue: TNotifySubscriptionType): INotifyConfig;
201 | begin
202 | Result := Self;
203 | FSubscriptionType := AValue;
204 |
205 | if AValue in [TNotifySubscriptionType.WEB_SOCKET] then
206 | FBaseURL := StringReplace(FBaseURL, 'https', 'wss', [rfReplaceAll]);
207 | end;
208 |
209 | function TNotifyConfig.SubscriptionType: TNotifySubscriptionType;
210 | begin
211 | Result := FSubscriptionType;
212 | end;
213 |
214 | function TNotifyConfig.SaveLog: Boolean;
215 | begin
216 | Result := FSaveLog;
217 | end;
218 |
219 | function TNotifyConfig.UserName: String;
220 | begin
221 | Result := FUserName;
222 | end;
223 |
224 | function TNotifyConfig.UserName(const AValue: String): INotifyConfig;
225 | begin
226 | Result := Self;
227 | FUserName := AValue;
228 | end;
229 |
230 | end.
231 |
--------------------------------------------------------------------------------
/src/Notify.Core.Contract.pas:
--------------------------------------------------------------------------------
1 | unit Notify.Core.Contract;
2 |
3 | interface
4 |
5 | uses
6 | System.SysUtils,
7 | Notify.Types,
8 | Notify.Api.Contract,
9 | Notify.Notification.Contract,
10 | Notify.Config.Contract,
11 | Notify.Action.Contract,
12 | Notify.Event.Contract,
13 | Notify.Attachment.Contract,
14 | Notify.Api.Response,
15 | System.Generics.Collections;
16 |
17 | type
18 | INotifyCore = interface
19 | ['{AEDB3C31-D45F-4469-9427-9CEA5427A4E3}']
20 | function Cache(const AValue: Boolean): INotifyCore;
21 | function UserName(const AValue: String): INotifyCore;
22 | function Password(const AValue: String): INotifyCore;
23 | function SaveLog(const AValue: Boolean): INotifyCore;
24 | function LogPath(const AValue: String): INotifyCore;
25 | function BaseURL(const AValue: String): INotifyCore;
26 | function SubscriptionType(const AValue: TNotifySubscriptionType): INotifyCore;
27 | function Topic(const AValue: String): INotifyCore;
28 | function DisableFireBase(const AValue: Boolean): INotifyCore;
29 | function Publish: INotifyCore;
30 | function Unsubscribe: INotifyCore;
31 | function Notification(const ANotification: INotifyNotification): INotifyCore; overload;
32 | procedure Subscribe(const ATopic: String; const ACallBack: TNotifyEventProc); overload;
33 | function Filter(const AFilterType: TNotifyFilter; const AValue: String): INotifyCore;
34 | function ClearFilters: INotifyCore;
35 | function Poll(const AValue: Boolean): INotifyCore; overload;
36 | function Since(const AValue: String): INotifyCore; overload;
37 | function Scheduled(const AValue: Boolean): INotifyCore; overload;
38 | function Response: TNotifyApiResponse;
39 | function Disconnect: INotifyCore;
40 | function Proxy(const AProxyServer, AProxyUser, AProxyPassword: String; const AProxyPort: Integer): INotifyCore; overload;
41 | end;
42 |
43 | INotifyCoreFacade = interface
44 | ['{47D19A0C-9C4C-4389-B2CD-CE4515BB4F35}']
45 | function Api: INotifyApi;
46 | function Notification: INotifyNotification;
47 | function Notify: INotifyCore;
48 | function Action: INotifyAction;
49 | function Config: INotifyConfig;
50 | function Event: INotifyEvent;
51 | function Attachment: INotifyAttachment;
52 | end;
53 |
54 | INotifyParametersFilters = TDictionary;
55 |
56 | implementation
57 |
58 | end.
59 |
--------------------------------------------------------------------------------
/src/Notify.Custom.Types.pas:
--------------------------------------------------------------------------------
1 | unit Notify.Custom.Types;
2 |
3 | interface
4 |
5 | uses
6 | REST.Json.Types;
7 |
8 | /// Custom types defined by the user. You can modify the classes members
9 | /// on this unit adjusting it for your needs. Do not rename the class
10 | /// declarations because it's being used underneath the library. You only need
11 | /// to rename fields/properties.
12 |
13 | type
14 |
15 | {$M+}
16 |
17 | TNotifyActionHeaders = class
18 | private
19 | [JSONName('cmd')]
20 | FCmd: String;
21 | [JSONName('parameter')]
22 | FParameter: String;
23 | [JSONName('systemdate')]
24 | FSystemDate: String;
25 | [JSONName('auth')]
26 | FAuth: String;
27 | published
28 | property Cmd: String read FCmd write FCmd;
29 | property Parameter: String read FParameter write FParameter;
30 | property SystemDate: String read FSystemDate write FSystemDate;
31 | property Auth: String read FAuth write FAuth;
32 | end;
33 |
34 | implementation
35 |
36 | end.
37 |
--------------------------------------------------------------------------------
/src/Notify.Error.pas:
--------------------------------------------------------------------------------
1 | unit Notify.Error;
2 |
3 | interface
4 |
5 | uses
6 | Notify.JSON.Parser, REST.Json.Types;
7 |
8 | type
9 | TNotifyErrors = class(TJsonDTO)
10 | private
11 | [JSONName('code')]
12 | FCode: Integer;
13 | [JSONName('http')]
14 | FHttp: Integer;
15 | [JSONName('error')]
16 | FError: String;
17 | published
18 | property Code: Integer read FCode write FCode;
19 | property Http: Integer read FHttp write FHttp;
20 | property Error: String read FError write FError;
21 | end;
22 |
23 | implementation
24 |
25 | end.
26 |
--------------------------------------------------------------------------------
/src/Notify.Event.Contract.pas:
--------------------------------------------------------------------------------
1 | unit Notify.Event.Contract;
2 |
3 | interface
4 |
5 | uses
6 | System.SysUtils,
7 | Notify.Types,
8 | Notify.Action.Contract,
9 | Notify.Attachment.Contract,
10 | System.Generics.Collections;
11 |
12 | type
13 |
14 | INotifyEventActions = TDictionary;
15 |
16 | INotifyEvent = interface
17 | ['{0342B585-BB88-4A63-9C41-8848B90B6042}']
18 | function Id: String; overload;
19 | function Id(const AValue: String): INotifyEvent; overload;
20 | function Time: Integer; overload;
21 | function Time(const AValue: Integer): INotifyEvent; overload;
22 | function Event: String; overload;
23 | function Event(const AValue: String): INotifyEvent; overload;
24 | function Topic: String; overload;
25 | function Topic(const AValue: String): INotifyEvent; overload;
26 | function Tags: TArray; overload;
27 | function Tags(const AValue: TArray): INotifyEvent; overload;
28 | function Click: String; overload;
29 | function Click(const AValue: String): INotifyEvent; overload;
30 | function Title: String; overload;
31 | function Title(const AValue: String): INotifyEvent; overload;
32 | function MessageContent: String; overload;
33 | function MessageContent(const AValue: String): INotifyEvent; overload;
34 | function Priority: TNotifyPriority; overload;
35 | function Priority(const AValue: TNotifyPriority): INotifyEvent; overload;
36 | function Action: INotifyAction; overload;
37 | function Action(const AValue: INotifyAction): INotifyEvent; overload;
38 | function Actions: INotifyEventActions;
39 | function Attachment: INotifyAttachment; overload;
40 | function Attachment(const AValue: INotifyAttachment): INotifyEvent; overload;
41 | function Icon: String; overload;
42 | function Icon(const AValue: String): INotifyEvent; overload;
43 | end;
44 |
45 | TNotifyEventProc = TProc;
46 |
47 | implementation
48 |
49 | end.
50 |
--------------------------------------------------------------------------------
/src/Notify.Event.DTO.pas:
--------------------------------------------------------------------------------
1 | unit Notify.Event.DTO;
2 |
3 | interface
4 |
5 | uses
6 | Rest.Json.Types,
7 | Notify.JSON.Parser,
8 | Notify.Action.DTO,
9 | Notify.Attachment.DTO,
10 | System.Generics.Collections;
11 |
12 | type
13 | TNotifyEventDTO = class(TJsonDTO)
14 | private
15 | [JSONName('id')]
16 | FId: String;
17 | [JSONName('time')]
18 | FTime: Integer;
19 | [JSONName('event')]
20 | FEvent: String;
21 | [JSONName('topic')]
22 | FTopic: String;
23 | [JSONName('priority')]
24 | FPriority: Integer;
25 | [JSONName('click')]
26 | FClick: String;
27 | [JSONName('title')]
28 | FTitle: String;
29 | [JSONName('message')]
30 | FMessage: String;
31 | [JSONName('icon')]
32 | FIcon: String;
33 | [JSONName('tags')]
34 | FTagsArray: TArray;
35 | [JSONMarshalled(False)]
36 | FTags: TList;
37 | [GenericListReflect]
38 | FActions: TObjectList;
39 | [JSONName('actions'), JSONMarshalled(False)]
40 | FActionsArray: TArray;
41 | [JSONName('attachment')]
42 | FAttachment: TNotifyAttachmentDTO;
43 | function GetTags: TList;
44 | function GetActions: TObjectList;
45 | protected
46 | function GetAsJson: string; override;
47 | published
48 | property Id: String read FId write FId;
49 | property Time: Integer read FTime write FTime;
50 | property Event: String read FEvent write FEvent;
51 | property Topic: String read FTopic write FTopic;
52 | property Priority: Integer read FPriority write FPriority;
53 | property Click: String read FClick write FClick;
54 | property Title: String read FTitle write FTitle;
55 | property Message: String read FMessage write FMessage;
56 | property Tags: TList read GetTags;
57 | property Actions: TObjectList read GetActions;
58 | property Attachment: TNotifyAttachmentDTO read FAttachment write FAttachment;
59 | property Icon: String read FIcon write FIcon;
60 | public
61 | constructor Create; override;
62 | destructor Destroy; override;
63 | end;
64 |
65 |
66 | implementation
67 |
68 | { TNotifyEventDTO }
69 |
70 | constructor TNotifyEventDTO.Create;
71 | begin
72 | inherited;
73 | end;
74 |
75 | destructor TNotifyEventDTO.Destroy;
76 | begin
77 | GetTags.Free;
78 | GetActions.Free;
79 | inherited;
80 | end;
81 |
82 | function TNotifyEventDTO.GetActions: TObjectList;
83 | begin
84 | Result := ObjectList(FActions, FActionsArray);
85 | end;
86 |
87 | function TNotifyEventDTO.GetAsJson: string;
88 | begin
89 | RefreshArray(FTags, FTagsArray);
90 | RefreshArray(FActions, FActionsArray);
91 | Result := inherited;
92 | end;
93 |
94 | function TNotifyEventDTO.GetTags: TList;
95 | begin
96 | Result := List(FTags, FTagsArray);
97 | end;
98 |
99 | end.
100 |
--------------------------------------------------------------------------------
/src/Notify.Event.pas:
--------------------------------------------------------------------------------
1 | unit Notify.Event;
2 |
3 | interface
4 |
5 | uses
6 | Notify.Types,
7 | Notify.Event.Contract,
8 | Notify.Action.Contract,
9 | Notify.Attachment.Contract;
10 |
11 | type
12 | TNotifyEvent = class(TInterfacedObject, INotifyEvent)
13 | private
14 | FId: String;
15 | FTime: Integer;
16 | FEvent: String;
17 | FTopic: String;
18 | FTags: TArray;
19 | FClick: String;
20 | FTitle: String;
21 | FMessage: String;
22 | FPriority: TNotifyPriority;
23 | FAction: INotifyAction;
24 | FActions: INotifyEventActions;
25 | FAttachment: INotifyAttachment;
26 | FIcon: String;
27 | public
28 | class function New: INotifyEvent;
29 | constructor Create;
30 | destructor Destroy; override;
31 | function Id: String; overload;
32 | function Id(const AValue: String): INotifyEvent; overload;
33 | function Time: Integer; overload;
34 | function Time(const AValue: Integer): INotifyEvent; overload;
35 | function Event: String; overload;
36 | function Event(const AValue: String): INotifyEvent; overload;
37 | function Topic: String; overload;
38 | function Topic(const AValue: String): INotifyEvent; overload;
39 | function Tags: TArray; overload;
40 | function Tags(const AValue: TArray): INotifyEvent; overload;
41 | function Click: String; overload;
42 | function Click(const AValue: String): INotifyEvent; overload;
43 | function Title: String; overload;
44 | function Title(const AValue: String): INotifyEvent; overload;
45 | function MessageContent: String; overload;
46 | function MessageContent(const AValue: String): INotifyEvent; overload;
47 | function Priority: TNotifyPriority; overload;
48 | function Priority(const AValue: TNotifyPriority): INotifyEvent; overload;
49 | function Action: INotifyAction; overload;
50 | function Action(const AValue: INotifyAction): INotifyEvent; overload;
51 | function Actions: INotifyEventActions;
52 | function Attachment: INotifyAttachment; overload;
53 | function Attachment(const AValue: INotifyAttachment): INotifyEvent; overload;
54 | function Icon: String; overload;
55 | function Icon(const AValue: String): INotifyEvent; overload;
56 | end;
57 |
58 | implementation
59 |
60 | uses
61 | Notify.Facade,
62 | System.Generics.Collections;
63 |
64 | { TNotifySubscription }
65 |
66 | function TNotifyEvent.Action(const AValue: INotifyAction): INotifyEvent;
67 | begin
68 | Result := Self;
69 | FAction := AValue;
70 |
71 | if FActions.ContainsKey(AValue.&Label) then
72 | FActions.Remove(AValue.&Label);
73 |
74 | if FActions.Count >= 3 then
75 | Exit;
76 |
77 | FActions.Add(AValue.&Label, AValue);
78 | end;
79 |
80 | function TNotifyEvent.Actions: INotifyEventActions;
81 | begin
82 | Result := FActions;
83 | end;
84 |
85 | function TNotifyEvent.Attachment(const AValue: INotifyAttachment): INotifyEvent;
86 | begin
87 | Result := Self;
88 | FAttachment := AValue;
89 | end;
90 |
91 | function TNotifyEvent.Attachment: INotifyAttachment;
92 | begin
93 | Result := FAttachment;
94 | end;
95 |
96 | function TNotifyEvent.Action: INotifyAction;
97 | begin
98 | Result := FAction;
99 | end;
100 |
101 | function TNotifyEvent.Click(const AValue: String): INotifyEvent;
102 | begin
103 | Result := Self;
104 | FClick := AValue;
105 | end;
106 |
107 | constructor TNotifyEvent.Create;
108 | begin
109 | FActions := TDictionary.Create();
110 | FAttachment := TNotifyCoreFacade.New.Attachment;
111 | end;
112 |
113 | destructor TNotifyEvent.Destroy;
114 | begin
115 | FActions.Free;
116 | inherited;
117 | end;
118 |
119 | function TNotifyEvent.Click: String;
120 | begin
121 | Result := FClick;
122 | end;
123 |
124 | function TNotifyEvent.Event: String;
125 | begin
126 | Result := FEvent;
127 | end;
128 |
129 | function TNotifyEvent.Event(const AValue: String): INotifyEvent;
130 | begin
131 | Result := Self;
132 | FEvent := AValue;
133 | end;
134 |
135 | function TNotifyEvent.Icon(const AValue: String): INotifyEvent;
136 | begin
137 | Result := Self;
138 | FIcon := AValue;
139 | end;
140 |
141 | function TNotifyEvent.Icon: String;
142 | begin
143 | Result := FIcon;
144 | end;
145 |
146 | function TNotifyEvent.Id(const AValue: String): INotifyEvent;
147 | begin
148 | Result := Self;
149 | FId := AValue;
150 | end;
151 |
152 | function TNotifyEvent.Id: String;
153 | begin
154 | Result := FId;
155 | end;
156 |
157 | function TNotifyEvent.MessageContent: String;
158 | begin
159 | Result := FMessage;
160 | end;
161 |
162 | function TNotifyEvent.MessageContent(const AValue: String): INotifyEvent;
163 | begin
164 | Result := Self;
165 | FMessage := AValue;
166 | end;
167 |
168 | class function TNotifyEvent.New: INotifyEvent;
169 | begin
170 | Result := Self.Create;
171 | end;
172 |
173 | function TNotifyEvent.Priority(const AValue: TNotifyPriority): INotifyEvent;
174 | begin
175 | Result := Self;
176 | FPriority := AValue;
177 | end;
178 |
179 | function TNotifyEvent.Priority: TNotifyPriority;
180 | begin
181 | Result := FPriority;
182 | end;
183 |
184 | function TNotifyEvent.Tags(const AValue: TArray): INotifyEvent;
185 | begin
186 | Result := Self;
187 | FTags := AValue;
188 | end;
189 |
190 | function TNotifyEvent.Tags: TArray;
191 | begin
192 | Result := FTags;
193 | end;
194 |
195 | function TNotifyEvent.Time: Integer;
196 | begin
197 | Result := FTime;
198 | end;
199 |
200 | function TNotifyEvent.Time(const AValue: Integer): INotifyEvent;
201 | begin
202 | Result := Self;
203 | FTime := AValue;
204 | end;
205 |
206 | function TNotifyEvent.Title(const AValue: String): INotifyEvent;
207 | begin
208 | Result := Self;
209 | FTitle := AValue;
210 | end;
211 |
212 | function TNotifyEvent.Title: String;
213 | begin
214 | Result := FTitle;
215 | end;
216 |
217 | function TNotifyEvent.Topic(const AValue: String): INotifyEvent;
218 | begin
219 | Result := Self;
220 | FTopic := AValue;
221 | end;
222 |
223 | function TNotifyEvent.Topic: String;
224 | begin
225 | Result := FTopic;
226 | end;
227 |
228 | end.
229 |
--------------------------------------------------------------------------------
/src/Notify.Facade.pas:
--------------------------------------------------------------------------------
1 | unit Notify.Facade;
2 |
3 | interface
4 |
5 | uses
6 | Notify.Core.Contract,
7 | Notify.Api.Contract,
8 | Notify.Notification.Contract,
9 | Notify.Action.Contract,
10 | Notify.Config.Contract,
11 | Notify.Event.Contract,
12 | Notify.Attachment.Contract;
13 |
14 | type
15 | TNotifyCoreFacade = class sealed(TInterfacedObject, INotifyCoreFacade)
16 | private
17 | FApiFactory: INotifyApiFactory;
18 | public
19 | constructor Create;
20 | class function New: INotifyCoreFacade;
21 | function Api: INotifyApi;
22 | function Notification: INotifyNotification;
23 | function Notify: INotifyCore;
24 | function Action: INotifyAction;
25 | function Config: INotifyConfig;
26 | function Event: INotifyEvent;
27 | function Attachment: INotifyAttachment;
28 | end;
29 |
30 | implementation
31 |
32 | uses
33 | Notify.Api.Factory,
34 | Notify.Notification,
35 | Notify.Core,
36 | Notify.Action,
37 | Notify.Config,
38 | Notify.Attachment,
39 | Notify.Event;
40 |
41 | { TNotifyCoreFacade }
42 |
43 | function TNotifyCoreFacade.Notify: INotifyCore;
44 | begin
45 | Result := TNotifyCore.New;
46 | end;
47 |
48 | function TNotifyCoreFacade.Event: INotifyEvent;
49 | begin
50 | Result := TNotifyEvent.New;
51 | end;
52 |
53 | function TNotifyCoreFacade.Action: INotifyAction;
54 | begin
55 | Result := TNofifyAction.New;
56 | end;
57 |
58 | function TNotifyCoreFacade.Config: INotifyConfig;
59 | begin
60 | Result := TNotifyConfig.New
61 | end;
62 |
63 | constructor TNotifyCoreFacade.Create;
64 | begin
65 | FApiFactory := TNotifyApiFactory.New;
66 | end;
67 |
68 | class function TNotifyCoreFacade.New: INotifyCoreFacade;
69 | begin
70 | Result := Self.Create;
71 | end;
72 |
73 | function TNotifyCoreFacade.Api: INotifyApi;
74 | begin
75 | Result := FApiFactory.Api;
76 | end;
77 |
78 | function TNotifyCoreFacade.Attachment: INotifyAttachment;
79 | begin
80 | Result := TNotifyAttachment.New;
81 | end;
82 |
83 | function TNotifyCoreFacade.Notification: INotifyNotification;
84 | begin
85 | Result := TNotifyNotification.New;
86 | end;
87 |
88 | end.
89 |
--------------------------------------------------------------------------------
/src/Notify.JSON.Parser.pas:
--------------------------------------------------------------------------------
1 | unit Notify.JSON.Parser;
2 |
3 | interface
4 |
5 | uses System.Classes, System.Json, Rest.Json, System.Generics.Collections, Rest.JsonReflect;
6 |
7 | {$M+}
8 |
9 | type
10 | TArrayMapper = class
11 | protected
12 | procedure RefreshArray(aSource: TList; var aDestination: TArray);
13 | function List(var aList: TList; aSource: TArray): TList;
14 | function ObjectList(var aList: TObjectList; aSource: TArray): TObjectList;
15 | public
16 | constructor Create; virtual;
17 | end;
18 |
19 | TJsonDTO = class(TArrayMapper)
20 | private
21 | FOptions: TJsonOptions;
22 | class procedure PrettyPrintPair(aJSONValue: TJSONPair; aOutputStrings: TStrings; Last: Boolean; Indent: Integer);
23 | class procedure PrettyPrintJSON(aJSONValue: TJsonValue; aOutputStrings: TStrings; Indent: Integer = 0); overload;
24 | class procedure PrettyPrintArray(aJSONValue: TJSONArray; aOutputStrings: TStrings; Last: Boolean; Indent: Integer);
25 | protected
26 | function GetAsJson: string; virtual;
27 | procedure SetAsJson(aValue: string); virtual;
28 | public
29 | constructor Create; override;
30 | class function PrettyPrintJSON(aJson: string): string; overload;
31 | function ToString: string; override;
32 | property AsJson: string read GetAsJson write SetAsJson;
33 | end;
34 |
35 | GenericListReflectAttribute = class(JsonReflectAttribute)
36 | public
37 | constructor Create;
38 | end;
39 |
40 | SuppressZeroAttribute = class(JsonReflectAttribute)
41 | public
42 | constructor Create;
43 | end;
44 |
45 | implementation
46 |
47 | uses System.Sysutils, System.JSONConsts, System.Rtti, System.DateUtils;
48 |
49 | { TJsonDTO }
50 |
51 | constructor TJsonDTO.Create;
52 | begin
53 | inherited;
54 | FOptions := [joDateIsUTC, joDateFormatISO8601, joIgnoreEmptyStrings, joIgnoreEmptyArrays];
55 | end;
56 |
57 | function TJsonDTO.GetAsJson: string;
58 | begin
59 | Result := TJson.ObjectToJsonString(Self, FOptions);
60 | end;
61 |
62 | const
63 | INDENT_SIZE = 2;
64 |
65 | class procedure TJsonDTO.PrettyPrintJSON(aJSONValue: TJsonValue; aOutputStrings: TStrings; Indent: Integer);
66 | var
67 | i: Integer;
68 | Ident: Integer;
69 | begin
70 | Ident := Indent + INDENT_SIZE;
71 | i := 0;
72 |
73 | if aJSONValue is TJSONObject then
74 | begin
75 | aOutputStrings.Add(StringOfChar(' ', Ident) + '{');
76 | for i := 0 to TJSONObject(aJSONValue).Count - 1 do
77 | PrettyPrintPair(TJSONObject(aJSONValue).Pairs[i], aOutputStrings, i = TJSONObject(aJSONValue).Count - 1, Ident);
78 |
79 | aOutputStrings.Add(StringOfChar(' ', Ident) + '}');
80 | end
81 | else if aJSONValue is TJSONArray then
82 | PrettyPrintArray(TJSONArray(aJSONValue), aOutputStrings, i = TJSONObject(aJSONValue).Count - 1, Ident)
83 | else
84 | aOutputStrings.Add(StringOfChar(' ', Ident) + aJSONValue.ToString);
85 | end;
86 |
87 | class procedure TJsonDTO.PrettyPrintArray(aJSONValue: TJSONArray; aOutputStrings: TStrings; Last: Boolean; Indent: Integer);
88 | var
89 | i: Integer;
90 | begin
91 | aOutputStrings.Add(StringOfChar(' ', Indent + INDENT_SIZE) + '[');
92 |
93 | for i := 0 to aJSONValue.Count - 1 do
94 | begin
95 | PrettyPrintJSON(aJSONValue.Items[i], aOutputStrings, Indent);
96 | if i < aJSONValue.Count - 1 then
97 | aOutputStrings[aOutputStrings.Count - 1] := aOutputStrings[aOutputStrings.Count - 1] + ',';
98 | end;
99 |
100 | aOutputStrings.Add(StringOfChar(' ', Indent + INDENT_SIZE - 2) + ']');
101 | end;
102 |
103 | class function TJsonDTO.PrettyPrintJSON(aJson: string): string;
104 | var
105 | StringList: TStringlist;
106 | JSONValue: TJsonValue;
107 | begin
108 | StringList := TStringlist.Create;
109 | try
110 | JSONValue := TJSONObject.ParseJSONValue(aJson);
111 | try
112 | if JSONValue <> nil then
113 | PrettyPrintJSON(JSONValue, StringList);
114 | finally
115 | JSONValue.Free;
116 | end;
117 |
118 | Result := StringList.Text;
119 | finally
120 | StringList.Free;
121 | end;
122 | end;
123 |
124 | class procedure TJsonDTO.PrettyPrintPair(aJSONValue: TJSONPair; aOutputStrings: TStrings; Last: Boolean; Indent: Integer);
125 | const
126 | TEMPLATE = '%s:%s';
127 | var
128 | Line: string;
129 | NewList: TStringlist;
130 | begin
131 | NewList := TStringlist.Create;
132 | try
133 | PrettyPrintJSON(aJSONValue.JSONValue, NewList, Indent);
134 | Line := Format(TEMPLATE, [aJSONValue.JsonString.ToString, Trim(NewList.Text)]);
135 | finally
136 | NewList.Free;
137 | end;
138 |
139 | Line := StringOfChar(' ', Indent + INDENT_SIZE) + Line;
140 | if not Last then
141 | Line := Line + ',';
142 | aOutputStrings.Add(Line);
143 | end;
144 |
145 | procedure TJsonDTO.SetAsJson(aValue: string);
146 | var
147 | JSONValue: TJsonValue;
148 | JSONObject: TJSONObject;
149 | begin
150 | JSONValue := TJSONObject.ParseJSONValue(aValue);
151 | try
152 | if not Assigned(JSONValue) then
153 | Exit;
154 |
155 | if (JSONValue is TJSONArray) then
156 | begin
157 | with TJSONUnMarshal.Create do
158 | try
159 | SetFieldArray(Self, 'Items', (JSONValue as TJSONArray));
160 | finally
161 | Free;
162 | end;
163 |
164 | Exit;
165 | end;
166 |
167 | if (JSONValue is TJSONObject) then
168 | JSONObject := JSONValue as TJSONObject
169 | else
170 | begin
171 | aValue := aValue.Trim;
172 | if (aValue = '') and not Assigned(JSONValue) or (aValue <> '') and Assigned(JSONValue) and JSONValue.Null then
173 | Exit
174 | else
175 | raise EConversionError.Create(SCannotCreateObject);
176 | end;
177 |
178 | TJson.JsonToObject(Self, JSONObject, FOptions);
179 | finally
180 | JSONValue.Free;
181 | end;
182 | end;
183 |
184 | function TJsonDTO.ToString: string;
185 | begin
186 | Result := AsJson;
187 | end;
188 |
189 | { TArrayMapper }
190 |
191 | constructor TArrayMapper.Create;
192 | begin
193 | inherited;
194 | end;
195 |
196 | function TArrayMapper.List(var aList: TList; aSource: TArray): TList;
197 | begin
198 | if aList = nil then
199 | begin
200 | aList := TList.Create;
201 | aList.AddRange(aSource);
202 | end;
203 |
204 | Exit(aList);
205 | end;
206 |
207 | function TArrayMapper.ObjectList(var aList: TObjectList; aSource: TArray): TObjectList;
208 | var
209 | Element: T;
210 | begin
211 | if aList = nil then
212 | begin
213 | aList := TObjectList.Create;
214 | for Element in aSource do
215 | aList.Add(Element);
216 | end;
217 |
218 | Exit(aList);
219 | end;
220 |
221 | procedure TArrayMapper.RefreshArray(aSource: TList; var aDestination: TArray);
222 | begin
223 | if aSource <> nil then
224 | aDestination := aSource.ToArray;
225 | end;
226 |
227 | type
228 | TGenericListFieldInterceptor = class(TJSONInterceptor)
229 | public
230 | function ObjectsConverter(Data: TObject; Field: string): TListOfObjects; override;
231 | end;
232 |
233 | { TListFieldInterceptor }
234 |
235 | function TGenericListFieldInterceptor.ObjectsConverter(Data: TObject; Field: string): TListOfObjects;
236 | var
237 | ctx: TRttiContext;
238 | List: TList;
239 | RttiProperty: TRttiProperty;
240 | begin
241 | RttiProperty := ctx.GetType(Data.ClassInfo).GetProperty(Copy(Field, 2, MAXINT));
242 | List := TList(RttiProperty.GetValue(Data).AsObject);
243 | Result := TListOfObjects(List.List);
244 | SetLength(Result, List.Count);
245 | end;
246 |
247 | constructor GenericListReflectAttribute.Create;
248 | begin
249 | inherited Create(ctObjects, rtObjects, TGenericListFieldInterceptor, nil, false);
250 | end;
251 |
252 | type
253 | TSuppressZeroDateInterceptor = class(TJSONInterceptor)
254 | public
255 | function StringConverter(Data: TObject; Field: string): string; override;
256 | procedure StringReverter(Data: TObject; Field: string; Arg: string); override;
257 | end;
258 |
259 | function TSuppressZeroDateInterceptor.StringConverter(Data: TObject; Field: string): string;
260 | var
261 | RttiContext: TRttiContext;
262 | Date: TDateTime;
263 | begin
264 | Date := RttiContext.GetType(Data.ClassType).GetField(Field).GetValue(Data).AsType;
265 | if Date = 0 then
266 | Result := string.Empty
267 | else
268 | Result := DateToISO8601(Date, True);
269 | end;
270 |
271 | procedure TSuppressZeroDateInterceptor.StringReverter(Data: TObject; Field, Arg: string);
272 | var
273 | RttiContext: TRttiContext;
274 | Date: TDateTime;
275 | begin
276 | if Arg.IsEmpty then
277 | Date := 0
278 | else
279 | Date := ISO8601ToDate(Arg, True);
280 |
281 | RttiContext.GetType(Data.ClassType).GetField(Field).SetValue(Data, Date);
282 | end;
283 |
284 | { SuppressZeroAttribute }
285 |
286 | constructor SuppressZeroAttribute.Create;
287 | begin
288 | inherited Create(ctString, rtString, TSuppressZeroDateInterceptor);
289 | end;
290 |
291 | end.
292 |
--------------------------------------------------------------------------------
/src/Notify.Logs.pas:
--------------------------------------------------------------------------------
1 | unit Notify.Logs;
2 |
3 | interface
4 |
5 | uses
6 | System.Classes;
7 |
8 | type
9 | TNotifyLogs = class sealed
10 | public
11 | class procedure Log(const APath: String; AValue: String = ''); overload;
12 | class procedure Log(const APath: String; AValue: TMemoryStream); overload;
13 | end;
14 |
15 | implementation
16 |
17 | uses
18 | System.SysUtils, System.DateUtils;
19 |
20 | { TNotifyLogs }
21 |
22 | class procedure TNotifyLogs.Log(const APath: String; AValue: String);
23 | var
24 | LStrings: TStringList;
25 | LLogFileName: String;
26 | begin
27 | LStrings := TStringList.Create;
28 | try
29 | LLogFileName := 'ntfy-' + FormatDateTime('dd.MM.yyyy', Now) + '.txt';
30 |
31 | if FileExists(LLogFileName) then
32 | LStrings.LoadFromFile(LLogFileName);
33 |
34 | LStrings.Add(Format('%s : %s', [DateTimeToStr(Now), AValue]) );
35 | LStrings.SaveToFile(LLogFileName);
36 | finally
37 | LStrings.Free;
38 | end;
39 |
40 | end;
41 |
42 | class procedure TNotifyLogs.Log(const APath: String; AValue: TMemoryStream);
43 | var
44 | LStrings: TStringList;
45 | LLogFileName: String;
46 | LDataString: String;
47 | begin
48 |
49 | if AValue = nil then
50 | Exit;
51 |
52 | LStrings := TStringList.Create;
53 | try
54 | LLogFileName := 'ntfy-' + FormatDateTime('dd.MM.yyyy', Now) + '.txt';
55 |
56 | if FileExists(LLogFileName) then
57 | LStrings.LoadFromFile(LLogFileName);
58 |
59 | SetString(LDataString, PAnsiChar(AValue.Memory), AValue.Size);
60 | LStrings.Add(Format('%s : %s', [DateTimeToStr(Now), LDataString]) );
61 | LStrings.SaveToFile(LLogFileName);
62 | finally
63 | LStrings.Free;
64 | end;
65 |
66 | end;
67 |
68 | end.
69 |
--------------------------------------------------------------------------------
/src/Notify.Notification.Contract.pas:
--------------------------------------------------------------------------------
1 | unit Notify.Notification.Contract;
2 |
3 | interface
4 |
5 | uses
6 | Notify.Types,
7 | Notify.Action.Contract;
8 |
9 | type
10 | INotifyTags = TArray;
11 |
12 | INotifyNotification = interface
13 | ['{BE2E83B7-C39E-4985-93F9-4468976B6AC5}']
14 | function Topic: String; overload;
15 | function Topic(const AValue: String): INotifyNotification; overload;
16 | function MessageContent: String; overload;
17 | function MessageContent(const AValue: String): INotifyNotification; overload;
18 | function Title: String; overload;
19 | function Title(const AValue: String): INotifyNotification; overload;
20 | function Tags: INotifyTags; overload;
21 | function Tags(const AValue: INotifyTags): INotifyNotification; overload;
22 | function Priority: TNotifyPriority; overload;
23 | function Priority(const AValue: TNotifyPriority): INotifyNotification; overload;
24 | function AttachURL(const AValue: String): INotifyNotification; overload;
25 | function FilePath: String; overload;
26 | function AttachFile(const AValue: String): INotifyNotification; overload;
27 | function FileName: String; overload;
28 | function Click: String overload;
29 | function Click(const AValue: String): INotifyNotification; overload;
30 | function Action: INotifyAction; overload;
31 | function Action(const AValue: INotifyAction): INotifyNotification; overload;
32 | function ClearActions: INotifyNotification; overload;
33 | function AsJSONString: String;
34 | function Email: String; overload;
35 | function Email(const AValue: String): INotifyNotification; overload;
36 | function Icon: String; overload;
37 | function Icon(const AValue: String): INotifyNotification; overload;
38 | function Delay: String; overload;
39 | function Delay(const AValue: String): INotifyNotification; overload;
40 | end;
41 |
42 | implementation
43 |
44 | end.
45 |
--------------------------------------------------------------------------------
/src/Notify.Notification.DTO.pas:
--------------------------------------------------------------------------------
1 | unit Notify.Notification.DTO;
2 |
3 | interface
4 |
5 | uses
6 | Notify.JSON.Parser,
7 | System.Generics.Collections,
8 | REST.Json.Types,
9 | Notify.Action.DTO;
10 |
11 | type
12 | TNotifyNotificationDTO = class(TJsonDTO)
13 | private
14 | [JSONName('attach')]
15 | FAttach: string;
16 | [JSONName('click')]
17 | FClick: string;
18 | [JSONName('filename')]
19 | FFilename: string;
20 | [JSONName('message')]
21 | FMessage: string;
22 | [JSONName('priority')]
23 | FPriority: Integer;
24 | [JSONName('tags')]
25 | FTagsArray: TArray;
26 | [JSONMarshalled(False)]
27 | FTags: TList;
28 | [JSONName('title')]
29 | FTitle: string;
30 | [JSONName('topic')]
31 | FTopic: string;
32 | [JSONName('delay')]
33 | FDelay: String;
34 | [JSONName('email')]
35 | FEmail: String;
36 | [GenericListReflect]
37 | FActions: TObjectList;
38 | [JSONName('actions'), JSONMarshalled(False)]
39 | FActionsArray: TArray;
40 | function GetTags: TList;
41 | function GetActions: TObjectList;
42 | protected
43 | function GetAsJson: string; override;
44 | published
45 | property Attach: string read FAttach write FAttach;
46 | property Click: string read FClick write FClick;
47 | property Filename: string read FFilename write FFilename;
48 | property MessageContent: string read FMessage write FMessage;
49 | property Priority: Integer read FPriority write FPriority;
50 | property Tags: TList read GetTags;
51 | property Title: string read FTitle write FTitle;
52 | property Topic: string read FTopic write FTopic;
53 | property Actions: TObjectList read GetActions;
54 | property Delay: String read FDelay write FDelay;
55 | property Email: String read FEmail write FEmail;
56 | public
57 | destructor Destroy; override;
58 | end;
59 |
60 | implementation
61 |
62 | { TNotifyNotificationDTO }
63 |
64 | destructor TNotifyNotificationDTO.Destroy;
65 | begin
66 | GetTags.Free;
67 | GetActions.Free;
68 | inherited;
69 | end;
70 |
71 | function TNotifyNotificationDTO.GetTags: TList;
72 | begin
73 | Result := List(FTags, FTagsArray);
74 | end;
75 |
76 | function TNotifyNotificationDTO.GetActions: TObjectList;
77 | begin
78 | Result := ObjectList(FActions, FActionsArray);
79 | end;
80 |
81 | function TNotifyNotificationDTO.GetAsJson: string;
82 | begin
83 | RefreshArray(FTags, FTagsArray);
84 | RefreshArray(FActions, FActionsArray);
85 | Result := inherited;
86 | end;
87 |
88 | end.
89 |
--------------------------------------------------------------------------------
/src/Notify.Notification.pas:
--------------------------------------------------------------------------------
1 | unit Notify.Notification;
2 |
3 | interface
4 |
5 | uses
6 | Notify.Types,
7 | Notify.Action.Contract,
8 | Notify.Api.Contract,
9 | Notify.Notification.Contract,
10 | System.Generics.Collections;
11 |
12 | type
13 | TNotifyNotification = class sealed(TInterfacedObject, INotifyNotification)
14 | strict private
15 | FTopic: String;
16 | FMessageContent: String;
17 | FTitle: String;
18 | FTags: INotifyTags;
19 | FPriority: TNotifyPriority;
20 | FClick: String;
21 | FAction: INotifyAction;
22 | FActions: TDictionary;
23 | FFileName: String;
24 | FFilePath: String;
25 | FAttachment: String;
26 | FEmail: String;
27 | FIcon: String;
28 | FDelay: String;
29 | public
30 | class function New: INotifyNotification;
31 | constructor Create;
32 | destructor Destroy; override;
33 | private
34 | function Topic: String; overload;
35 | function Topic(const AValue: String): INotifyNotification; overload;
36 | function MessageContent: String; overload;
37 | function MessageContent(const AValue: String): INotifyNotification; overload;
38 | function Title: String; overload;
39 | function Title(const AValue: String): INotifyNotification; overload;
40 | function Tags: INotifyTags; overload;
41 | function Tags(const AValue: INotifyTags): INotifyNotification; overload;
42 | function Priority: TNotifyPriority; overload;
43 | function Priority(const AValue: TNotifyPriority): INotifyNotification; overload;
44 | function AttachURL(const AValue: String): INotifyNotification; overload;
45 | function FileName: String; overload;
46 | function FilePath: String; overload;
47 | function AttachFile(const AValue: String): INotifyNotification; overload;
48 | function Click: String overload;
49 | function Click(const AValue: String): INotifyNotification; overload;
50 | function Action: INotifyAction; overload;
51 | function Action(const AValue: INotifyAction): INotifyNotification; overload;
52 | function ClearActions: INotifyNotification; overload;
53 | function Email: String; overload;
54 | function Email(const AValue: String): INotifyNotification; overload;
55 | function Icon: String; overload;
56 | function Icon(const AValue: String): INotifyNotification; overload;
57 | function Delay: String; overload;
58 | function Delay(const AValue: String): INotifyNotification; overload;
59 | function AsJSONString: String;
60 | end;
61 |
62 | implementation
63 |
64 | uses
65 | System.SysUtils,
66 | Notify.SmartPointer,
67 | Notify.Action.DTO,
68 | Notify.Notification.DTO,
69 | System.Classes,
70 | Notify.Config;
71 |
72 | { TNotifyNotification }
73 |
74 | function TNotifyNotification.Action: INotifyAction;
75 | begin
76 | Result := FAction;
77 | end;
78 |
79 | function TNotifyNotification.Action(const AValue: INotifyAction): INotifyNotification;
80 | begin
81 | Result := Self;
82 | FAction := AValue;
83 |
84 | if FActions.ContainsKey(AValue.&Label) then
85 | FActions.Remove(AValue.&Label);
86 |
87 | if FActions.Count >= 3 then
88 | Exit;
89 |
90 | FActions.Add(AValue.&Label, AValue);
91 |
92 | end;
93 |
94 | function TNotifyNotification.AsJSONString: String;
95 | var
96 | LNotificationDTO: TSmartPointer;
97 | LActionDTO: TNotifyActionDTO;
98 | LAction: INotifyAction;
99 | begin
100 | LNotificationDTO.Value.Topic := FTopic;
101 | LNotificationDTO.Value.MessageContent := FMessageContent;
102 | LNotificationDTO.Value.Priority := Ord(FPriority);
103 | LNotificationDTO.Value.Title := FTitle;
104 | LNotificationDTO.Value.Tags.AddRange(FTags);
105 | LNotificationDTO.Value.Filename := FFileName;
106 | LNotificationDTO.Value.Attach := FAttachment;
107 | LNotificationDTO.Value.Delay := FDelay;
108 | LNotificationDTO.Value.Email := FEmail;
109 |
110 | for LAction in FActions.Values do
111 | begin
112 | LActionDTO := TNotifyActionDTO.Create;
113 | LActionDTO.Action := NotifyActionTypesArray[LAction.&Type];
114 | LActionDTO.&Label := LAction.&Label;
115 | LActionDTO.Clear := LAction.Clear;
116 | LActionDTO.Url := LAction.Url;
117 | LActionDTO.Method := LAction.Method;
118 | LActionDTO.Body := LAction.Body;
119 | LActionDTO.Method := LAction.Method;
120 | LActionDTO.Headers := LAction.Headers;
121 | LAction.Validate;
122 | LNotificationDTO.Value.Actions.Add(LActionDTO);
123 | end;
124 |
125 | Result := LNotificationDTO.Value.AsJson;
126 | end;
127 |
128 | function TNotifyNotification.AttachURL(const AValue: String): INotifyNotification;
129 | begin
130 | Result := Self;
131 | FAttachment := AValue;
132 | end;
133 |
134 | function TNotifyNotification.ClearActions: INotifyNotification;
135 | begin
136 | Result := Self;
137 | FActions.Clear;
138 | end;
139 |
140 | function TNotifyNotification.Click(const AValue: String): INotifyNotification;
141 | begin
142 | Result := Self;
143 | FClick := AValue;
144 | end;
145 |
146 | constructor TNotifyNotification.Create;
147 | begin
148 | FPriority := TNotifyPriority.DEFAULT;
149 | FActions := TDictionary.Create();
150 | end;
151 |
152 | function TNotifyNotification.Delay(const AValue: String): INotifyNotification;
153 | begin
154 | Result := Self;
155 | FDelay := AValue;
156 | end;
157 |
158 | function TNotifyNotification.Delay: String;
159 | begin
160 | Result := FDelay;
161 | end;
162 |
163 | destructor TNotifyNotification.Destroy;
164 | begin
165 | FActions.Free;
166 | inherited;
167 | end;
168 |
169 | function TNotifyNotification.Email(const AValue: String): INotifyNotification;
170 | begin
171 | //"Email" doesn't works together with "Delay"
172 | Result := Self;
173 | FEmail := AValue;
174 | end;
175 |
176 | function TNotifyNotification.Email: String;
177 | begin
178 | Result := FEmail;
179 | end;
180 |
181 | function TNotifyNotification.FilePath: String;
182 | begin
183 | Result := FFilePath;
184 | end;
185 |
186 | function TNotifyNotification.Click: String;
187 | begin
188 | Result := FClick;
189 | end;
190 |
191 | function TNotifyNotification.FileName: String;
192 | begin
193 | Result := FFileName;
194 | end;
195 |
196 | function TNotifyNotification.AttachFile(const AValue: String): INotifyNotification;
197 | begin
198 | Result := Self;
199 | FFileName := ExtractFileName(AValue);
200 | FFilePath := AValue;
201 | end;
202 |
203 | function TNotifyNotification.Icon(const AValue: String): INotifyNotification;
204 | begin
205 | Result := Self;
206 | FIcon := AValue;
207 | end;
208 |
209 | function TNotifyNotification.Icon: String;
210 | begin
211 | Result := FIcon;
212 | end;
213 |
214 | function TNotifyNotification.MessageContent(const AValue: String): INotifyNotification;
215 | begin
216 | Result := Self;
217 | FMessageContent := AValue;
218 | end;
219 |
220 | class function TNotifyNotification.New: INotifyNotification;
221 | begin
222 | Result := Self.Create;
223 | end;
224 |
225 | function TNotifyNotification.MessageContent: String;
226 | begin
227 | Result := FMessageContent;
228 | end;
229 |
230 | function TNotifyNotification.Priority(const AValue: TNotifyPriority): INotifyNotification;
231 | begin
232 | Result := Self;
233 | FPriority := AValue;
234 | end;
235 |
236 | function TNotifyNotification.Priority: TNotifyPriority;
237 | begin
238 | Result := FPriority;
239 | end;
240 |
241 | function TNotifyNotification.Tags: INotifyTags;
242 | begin
243 | Result := FTags;
244 | end;
245 |
246 | function TNotifyNotification.Tags(const AValue: INotifyTags): INotifyNotification;
247 | begin
248 | Result := Self;
249 | FTags := AValue;
250 | end;
251 |
252 | function TNotifyNotification.Title(const AValue: String): INotifyNotification;
253 | begin
254 | Result := Self;
255 | FTitle := AValue;
256 | end;
257 |
258 | function TNotifyNotification.Title: String;
259 | begin
260 | Result := FTitle;
261 | end;
262 |
263 | function TNotifyNotification.Topic(const AValue: String): INotifyNotification;
264 | begin
265 | Result := Self;
266 | FTopic := AValue;
267 | end;
268 |
269 | function TNotifyNotification.Topic: String;
270 | begin
271 | Result := FTopic;
272 | end;
273 |
274 | end.
275 |
--------------------------------------------------------------------------------
/src/Notify.Response.Data.pas:
--------------------------------------------------------------------------------
1 | unit Notify.Response.Data;
2 |
3 | interface
4 |
5 | uses
6 | REST.Json.Types, Notify.JSON.Parser;
7 |
8 | type
9 | TNotifyResponseDTO = class(TJsonDTO)
10 | private
11 | [JSONName('id')]
12 | FId: String;
13 | [JSONName('time')]
14 | FTime: Integer;
15 | published
16 | property Id: String read FId write FId;
17 | property Time: Integer read FTime write FTime;
18 | end;
19 |
20 | implementation
21 |
22 | end.
23 |
--------------------------------------------------------------------------------
/src/Notify.SmartPointer.pas:
--------------------------------------------------------------------------------
1 | unit Notify.SmartPointer;
2 |
3 | interface
4 |
5 | uses
6 | Generics.Defaults;
7 |
8 | type
9 | TSmartPointer = record
10 | strict private
11 | FValue: T;
12 | FFreeTheValue: IInterface;
13 | function GetValue: T;
14 | private
15 | type
16 | TFreeTheValue = class (TInterfacedObject)
17 | private
18 | FObjectToFree: TObject;
19 | public
20 | constructor Create(anObjectToFree: TObject);
21 | destructor Destroy; override;
22 | end;
23 | public
24 | constructor Create(AValue: T); overload;
25 | procedure Create; overload;
26 | class operator Implicit(AValue: T): TSmartPointer;
27 | class operator Implicit(smart: TSmartPointer ): T;
28 | property Value: T read GetValue;
29 | end;
30 |
31 | implementation
32 |
33 |
34 | { TSmartPointer }
35 |
36 | constructor TSmartPointer.Create(AValue: T);
37 | begin
38 | FValue := AValue;
39 | FFreeTheValue := TFreeTheValue.Create(FValue);
40 | end;
41 |
42 | procedure TSmartPointer.Create;
43 | begin
44 | TSmartPointer.Create (T.Create);
45 | end;
46 |
47 | function TSmartPointer.GetValue: T;
48 | begin
49 | if not Assigned(FFreeTheValue) then
50 | Self := TSmartPointer.Create (T.Create);
51 | Result := FValue;
52 | end;
53 |
54 | class operator TSmartPointer.Implicit(smart: TSmartPointer): T;
55 | begin
56 | Result := Smart.Value;
57 | end;
58 |
59 | class operator TSmartPointer.Implicit(AValue: T): TSmartPointer;
60 | begin
61 | Result := TSmartPointer.Create(AValue);
62 | end;
63 |
64 | { TSmartPointer.TFreeTheValue }
65 |
66 | constructor TSmartPointer.TFreeTheValue.Create(anObjectToFree: TObject);
67 | begin
68 | FObjectToFree := anObjectToFree;
69 | end;
70 |
71 | destructor TSmartPointer.TFreeTheValue.Destroy;
72 | begin
73 | FObjectToFree.Free;
74 | inherited;
75 | end;
76 |
77 | end.
78 |
--------------------------------------------------------------------------------
/src/Notify.Subscription.Event.pas:
--------------------------------------------------------------------------------
1 | unit Notify.Subscription.Event;
2 |
3 | interface
4 |
5 | type
6 | TNotifySubscriptionEvent = type UTF8String;
7 |
8 | implementation
9 |
10 | end.
11 |
--------------------------------------------------------------------------------
/src/Notify.Types.pas:
--------------------------------------------------------------------------------
1 | unit Notify.Types;
2 |
3 | interface
4 |
5 | uses
6 | Notify.JSON.Parser, System.Generics.Collections;
7 |
8 | type
9 | {$SCOPEDENUMS ON}
10 |
11 | TNotifyActionType = (VIEW, BROADCAST, HTTP);
12 | TNotifySubscriptionType = (JSON, SSE, RAW, WEB_SOCKET);
13 |
14 | TNotifyPriority = (
15 | MAX = 5,
16 | HIGH = 4,
17 | DEFAULT = 3,
18 | LOW = 2,
19 | MIN = 1
20 | );
21 |
22 | TNotifyMessageEvent = (
23 | OPEN,
24 | KEEPALIVE,
25 | MSG,
26 | POLL_REQUEST
27 | );
28 |
29 | TNotifyFilter = (
30 | ID,
31 | TITLE,
32 | MESSAGECONTENT,
33 | PRIORITY,
34 | TAGS
35 | );
36 |
37 | {$SCOPEDENUMS OFF}
38 |
39 | TJsonDTO = Notify.JSON.Parser.TJsonDTO;
40 |
41 | const
42 | NotifyActionTypesArray: array [TNotifyActionType] of String = (
43 | 'view',
44 | 'broadcast',
45 | 'http'
46 | );
47 |
48 | NotifyMessageEventArray: array[TNotifyMessageEvent] of String = (
49 | 'open',
50 | 'keepalive',
51 | 'message',
52 | 'poll_request'
53 | );
54 |
55 | NotifyFilterTypeDescription: array[TNotifyFilter] of String = (
56 | 'id',
57 | 'title',
58 | 'message',
59 | 'priority',
60 | 'tags'
61 | );
62 |
63 | implementation
64 |
65 |
66 | end.
67 |
--------------------------------------------------------------------------------
/src/Notify.pas:
--------------------------------------------------------------------------------
1 | unit Notify;
2 |
3 | interface
4 |
5 | uses
6 | Notify.Types,
7 | Notify.Action.Contract,
8 | Notify.Notification.Contract,
9 | Notify.Event.Contract,
10 | Notify.Core.Contract,
11 | Notify.Api.Response;
12 |
13 | type
14 | INotify = Notify.Core.Contract.INotifyCore;
15 | INotifyNotification = Notify.Notification.Contract.INotifyNotification;
16 | INotifyAction = Notify.Action.Contract.INotifyAction;
17 | INotifyEvent = Notify.Event.Contract.INotifyEvent;
18 | TNotifyEventProc = Notify.Event.Contract.TNotifyEventProc;
19 | TNotifyPriority = Notify.Types.TNotifyPriority;
20 | TNotifyActionType = Notify.Types.TNotifyActionType;
21 | TNotifySubscriptionType = Notify.Types.TNotifySubscriptionType;
22 | TJsonDTO = Notify.Types.TJsonDTO;
23 | TNotifyFilter = Notify.Types.TNotifyFilter;
24 | TNotifyApiResponse = Notify.Api.Response.TNotifyApiResponse;
25 |
26 | var
27 | New: INotifyCoreFacade;
28 | Ntfy: INotify;
29 |
30 | implementation
31 |
32 | uses
33 | Notify.Facade;
34 |
35 | initialization
36 | New := nil;
37 | Ntfy := nil;
38 | New := TNotifyCoreFacade.New;
39 | Ntfy := New.Notify;
40 |
41 |
42 | end.
43 |
--------------------------------------------------------------------------------
/tests/NtfyForDelphiTests.dpr:
--------------------------------------------------------------------------------
1 | program NtfyForDelphiTests;
2 | {
3 |
4 | Delphi DUnit Test Project
5 | -------------------------
6 | This project contains the DUnit test framework and the GUI/Console test runners.
7 | Add "CONSOLE_TESTRUNNER" to the conditional defines entry in the project options
8 | to use the console test runner. Otherwise the GUI test runner will be used by
9 | default.
10 |
11 | }
12 |
13 | {$IFDEF CONSOLE_TESTRUNNER}
14 | {$APPTYPE CONSOLE}
15 | {$ENDIF}
16 |
17 | uses
18 | DUnitTestRunner,
19 | Notify in '..\src\Notify.pas',
20 | Notify.Action.Contract in '..\src\Notify.Action.Contract.pas',
21 | Notify.Action.DTO in '..\src\Notify.Action.DTO.pas',
22 | Notify.Action in '..\src\Notify.Action.pas',
23 | Notify.Api.Contract in '..\src\Notify.Api.Contract.pas',
24 | Notify.Api.Indy in '..\src\Notify.Api.Indy.pas',
25 | Notify.Attachment.Contract in '..\src\Notify.Attachment.Contract.pas',
26 | Notify.Attachment.DTO in '..\src\Notify.Attachment.DTO.pas',
27 | Notify.Attachment in '..\src\Notify.Attachment.pas',
28 | Notify.Config.Contract in '..\src\Notify.Config.Contract.pas',
29 | Notify.Config in '..\src\Notify.Config.pas',
30 | Notify.Core.Contract in '..\src\Notify.Core.Contract.pas',
31 | Notify.Core in '..\src\Notify.Core.pas',
32 | Notify.Event.Contract in '..\src\Notify.Event.Contract.pas',
33 | Notify.Event.DTO in '..\src\Notify.Event.DTO.pas',
34 | Notify.Event in '..\src\Notify.Event.pas',
35 | Notify.Facade in '..\src\Notify.Facade.pas',
36 | Notify.JSON.Parser in '..\src\Notify.JSON.Parser.pas',
37 | Notify.Logs in '..\src\Notify.Logs.pas',
38 | Notify.Notification.Contract in '..\src\Notify.Notification.Contract.pas',
39 | Notify.Notification.DTO in '..\src\Notify.Notification.DTO.pas',
40 | Notify.Notification in '..\src\Notify.Notification.pas',
41 | Notify.SmartPointer in '..\src\Notify.SmartPointer.pas',
42 | Notify.Subscription.Event in '..\src\Notify.Subscription.Event.pas',
43 | Notify.Types in '..\src\Notify.Types.pas',
44 | Notify.Error in '..\src\Notify.Error.pas',
45 | Notify.Api.Response in '..\src\Notify.Api.Response.pas',
46 | Notify.Response.Data in '..\src\Notify.Response.Data.pas',
47 | Notify.Custom.Types in '..\src\Notify.Custom.Types.pas',
48 | Notify.Api.NetHTTP in '..\src\Notify.Api.NetHTTP.pas',
49 | Notify.Api.Factory in '..\src\Notify.Api.Factory.pas',
50 | NX.Horizon in '..\src\NX.Horizon.pas',
51 | Test.Simple.Message in 'src\Test.Simple.Message.pas',
52 | Test.Action.Header in 'src\Test.Action.Header.pas',
53 | Test.Constants in 'src\Test.Constants.pas',
54 | Test.Action.HTTP in 'src\Test.Action.HTTP.pas',
55 | Test.Action.View in 'src\Test.Action.View.pas',
56 | Test.Attachments in 'src\Test.Attachments.pas',
57 | Test.Email in 'src\Test.Email.pas',
58 | Test.Icons in 'src\Test.Icons.pas',
59 | Test.Emojis in 'src\Test.Emojis.pas',
60 | Test.URL.Attachments in 'src\Test.URL.Attachments.pas',
61 | Index in 'src\Index.pas',
62 | Test.Subscription in 'src\Test.Subscription.pas';
63 |
64 | begin
65 | DUnitTestRunner.RunRegisteredTests;
66 | Readln;
67 | end.
68 |
69 |
--------------------------------------------------------------------------------
/tests/NtfyForDelphiTests.res:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hazzelnuts/ntfy-for-delphi/fd0d51efafd6954bfd1aaaae98fc07531149bb6a/tests/NtfyForDelphiTests.res
--------------------------------------------------------------------------------
/tests/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | ## Test cases
6 |
7 | DUnit tests for basic methods. You can deploy a self-hosted server via Docker images. Here you find a demo docker compose and server yml file.
8 |
9 | ## Environment
10 | Run these steps in your enviroment:
11 |
12 | ``` cmd
13 | docker compose create
14 | docker cp server.yml ntfy:/etc/ntfy
15 | docker compose up
16 | ```
17 |
18 | ## Installation
19 | * Open ```sample/Examples.grouppoj``` and build ```NtfyForDelphiTests``` project.
20 | * (Optional) Paste the SSL libraries into the executable's folder ```bin``` if you are using ```NTFY_HTTP_INDY```.
21 |
22 | ## DUnit Tests
23 | * Access http://localhost:80 in your browser and subscribe to ```notify-delphi-integration-8jh27d```.
24 | * Run ```bin\NtfyForDelphiTests.exe```. You should receive notifications sent from the test runner.
25 |
26 | ``` pascal
27 | RegisterTest('Send simple message', TTestSimpleMessage.Suite);
28 | RegisterTest('Send action view', TTestActionView.Suite);
29 | RegisterTest('Send action http', TTestActionHTTP.Suite);
30 | RegisterTest('Send file attachments', TTestAttachments.Suite);
31 | RegisterTest('Send action headers', TTestActionHeader.Suite);
32 | RegisterTest('Send emails', TTestEmail.Suite);
33 | RegisterTest('Send tags', TTestEmojis.Suite);
34 | RegisterTest('Send icons', TTestIcons.Suite);
35 | RegisterTest('Send url attachments', TTestURLAttachments.Suite);
36 | ```
37 |
--------------------------------------------------------------------------------
/tests/docker-compose.yml:
--------------------------------------------------------------------------------
1 | version: "2.1"
2 |
3 | services:
4 | ntfy:
5 | image: binwiederhier/ntfy
6 | container_name: ntfy
7 | command:
8 | - serve
9 | volumes:
10 | - /var/cache/ntfy:/var/cache/ntfy
11 | - /etc/ntfy:/etc/ntfy
12 | ports:
13 | - 80:80
14 | restart: unless-stopped
15 |
--------------------------------------------------------------------------------
/tests/server.yml:
--------------------------------------------------------------------------------
1 | base-url: "http://localhost:80"
2 | attachment-cache-dir: "/var/cache/ntfy/attachments"
3 | attachment-total-size-limit: "5G"
4 | attachment-file-size-limit: "15M"
5 | attachment-expiry-duration: "3h"
6 | visitor-attachment-total-size-limit: "100M"
7 | visitor-attachment-daily-bandwidth-limit: "1000M"
8 | smtp-sender-addr: "email-smtp.us-east-2.amazonaws.com:587"
9 | smtp-sender-user: "AKIDEADBEEFAFFE12345"
10 | smtp-sender-pass: "Abd13Kf+sfAk2DzifjafldkThisIsNotARealKeyOMG."
11 | smtp-sender-from: "ntfy@ntfy.sh"
--------------------------------------------------------------------------------
/tests/src/Index.pas:
--------------------------------------------------------------------------------
1 | unit Index;
2 |
3 | interface
4 |
5 | uses
6 | Test.Simple.Message,
7 | Test.Action.Header,
8 | Test.Action.HTTP,
9 | Test.Action.View,
10 | Test.Attachments,
11 | Test.Email,
12 | Test.Emojis,
13 | Test.Icons,
14 | Test.URL.Attachments,
15 | Test.Subscription;
16 |
17 | implementation
18 |
19 | uses
20 | TestFramework;
21 |
22 | initialization
23 | RegisterTest('Send simple message', TTestSimpleMessage.Suite);
24 | RegisterTest('Send action view', TTestActionView.Suite);
25 | RegisterTest('Send action http', TTestActionHTTP.Suite);
26 | RegisterTest('Send file attachments', TTestAttachments.Suite);
27 | RegisterTest('Send action headers', TTestActionHeader.Suite);
28 | RegisterTest('Send emails', TTestEmail.Suite);
29 | RegisterTest('Send tags', TTestEmojis.Suite);
30 | RegisterTest('Send icons', TTestIcons.Suite);
31 | RegisterTest('Send url attachments', TTestURLAttachments.Suite);
32 | //RegisterTest('Subscription', TTestSubscription.Suite);
33 |
34 |
35 | end.
36 |
--------------------------------------------------------------------------------
/tests/src/Test.Action.HTTP.pas:
--------------------------------------------------------------------------------
1 | unit Test.Action.HTTP;
2 |
3 | interface
4 |
5 | uses
6 | TestFramework, Notify;
7 |
8 | type
9 | TTestActionHTTP = class(TTestCase)
10 | private
11 | FActionType: TNotifyActionType;
12 | FLabel: String;
13 | FUrl: String;
14 | procedure CallBack(AEvent: INotifyEvent);
15 | public
16 | procedure SetUp; override;
17 | published
18 | procedure Publish;
19 | procedure Subscribe;
20 | end;
21 |
22 | implementation
23 |
24 | uses
25 | Test.Constants, System.SysUtils;
26 |
27 | var
28 | Id, Time: String;
29 |
30 | { TTestActionHTTP }
31 |
32 | procedure TTestActionHTTP.CallBack(AEvent: INotifyEvent);
33 | begin
34 | FLabel := AEvent.Actions.Items[ACTION_LABEL].&Label;
35 | FUrl := AEvent.Actions.Items[ACTION_LABEL].Url;
36 | FActionType := AEvent.Actions.Items[ACTION_LABEL].&Type;
37 | end;
38 |
39 | procedure TTestActionHTTP.Publish;
40 | begin
41 | Ntfy := New.Notify;
42 | Ntfy.Notification(
43 | New.Notification
44 | .Topic(TOPIC)
45 | .Action(New.Action
46 | .&Type(TNotifyActionType.HTTP)
47 | .&Label(ACTION_LABEL)
48 | .Url(ACTION_URL)
49 | ));
50 |
51 | try
52 | try
53 | WriteLn('Action http test: Publish');
54 | Sleep(TIME_DELAY);
55 | Ntfy.ClearFilters;
56 | Ntfy.BaseURL('http://localhost:80');
57 | Ntfy.Publish;
58 | Id := Ntfy.Response.Data.Id;
59 | Time := Ntfy.Response.Data.Time.ToString;
60 | except on E: Exception do
61 | WriteLn(E.Message);
62 | end;
63 | finally
64 | CheckEquals(
65 | Ord(TStatusCode.OK),
66 | Ntfy.Response.StatusCode,
67 | MSG_REQUEST_FAILED
68 | );
69 | end;
70 | end;
71 |
72 | procedure TTestActionHTTP.SetUp;
73 | begin
74 |
75 | end;
76 |
77 | procedure TTestActionHTTP.Subscribe;
78 | begin
79 | try
80 | try
81 | WriteLn('Action http test: Subscribe');
82 | Sleep(TIME_DELAY);
83 | Ntfy := New.Notify;
84 | Ntfy.Poll(True);
85 | Ntfy.Since(Time);
86 | Ntfy.BaseURL('http://localhost:80');
87 | Ntfy.Subscribe(TOPIC, CallBack);
88 | finally
89 | Ntfy.Unsubscribe;
90 | end;
91 | finally
92 | CheckEquals(Ord(FActionType), Ord(TNotifyActionType.HTTP), MSG_WRONG_HEADERS);
93 | CheckEquals(FLabel, ACTION_LABEL, MSG_WRONG_LABELS);
94 | CheckEquals(FUrl, ACTION_URL, MSG_WRONG_URLS);
95 | end;
96 | end;
97 |
98 | end.
99 |
--------------------------------------------------------------------------------
/tests/src/Test.Action.Header.pas:
--------------------------------------------------------------------------------
1 | unit Test.Action.Header;
2 |
3 | interface
4 |
5 | uses
6 | TestFrameWork, Notify.Custom.Types, Notify;
7 |
8 | type
9 |
10 | TTestActionHeader = class(TTestCase)
11 | private
12 | FHeaders: TNotifyActionHeaders;
13 | FEventHeaders: TNotifyActionHeaders;
14 | procedure CallBack(AEvent: INotifyEvent);
15 | public
16 | procedure SetUp; override;
17 | procedure TearDown; override;
18 | published
19 | procedure Publish;
20 | procedure Subscribe;
21 | end;
22 |
23 | implementation
24 |
25 | uses
26 | Test.Constants, System.SysUtils;
27 |
28 | var
29 | Id, Time: String;
30 |
31 | { TestActionHeader }
32 |
33 | procedure TTestActionHeader.CallBack(AEvent: INotifyEvent);
34 | begin
35 | FEventHeaders := AEvent.Actions.Items[ACTION_LABEL].EventHeaders;
36 | end;
37 |
38 | procedure TTestActionHeader.Publish;
39 | begin
40 |
41 | Ntfy := New.Notify;
42 | Ntfy.Notification(
43 | New.Notification
44 | .Topic(TOPIC)
45 | .Action(New.Action
46 | .&Type(TNotifyActionType.VIEW)
47 | .&Label(ACTION_LABEL)
48 | .Url(ACTION_URL)
49 | .Headers(FHeaders)
50 | ));
51 |
52 | try
53 | try
54 | WriteLn('Action headers test: Publish');
55 | Sleep(TIME_DELAY);
56 | Ntfy.BaseURL('http://localhost:80');
57 | Ntfy.ClearFilters;
58 | Ntfy.Publish;
59 | Id := Ntfy.Response.Data.Id;
60 | Time := Ntfy.Response.Data.Time.ToString;
61 | except on E: Exception do
62 | WriteLn(E.Message);
63 | end;
64 | finally
65 | CheckEquals(
66 | Ord(TStatusCode.OK),
67 | Ntfy.Response.StatusCode,
68 | MSG_REQUEST_FAILED
69 | );
70 | end;
71 |
72 | end;
73 |
74 | procedure TTestActionHeader.SetUp;
75 | begin
76 | inherited;
77 | FHeaders := TNotifyActionHeaders.Create;
78 | FHeaders.Cmd := 'systeminfo';
79 | FHeaders.Parameter := '/FO LIST';
80 | FHeaders.SystemDate := 'date';
81 | end;
82 |
83 | procedure TTestActionHeader.Subscribe;
84 | begin
85 | try
86 | try
87 | WriteLn('Action headers test: Subscribe');
88 | Sleep(TIME_DELAY);
89 | Ntfy := New.Notify;
90 | Ntfy.BaseURL('http://localhost:80');
91 | Ntfy.Poll(True);
92 | Ntfy.Since(Time);
93 | Ntfy.Subscribe(TOPIC, CallBack);
94 | finally
95 | Ntfy.Unsubscribe;
96 | end;
97 | finally
98 | CheckEquals(FHeaders.Cmd, FEventHeaders.Cmd, MSG_WRONG_HEADERS);
99 | CheckEquals(FHeaders.Parameter, FEventHeaders.Parameter, MSG_WRONG_HEADERS);
100 | CheckEquals(FHeaders.SystemDate, FEventHeaders.SystemDate, MSG_WRONG_HEADERS);
101 | end;
102 | end;
103 |
104 | procedure TTestActionHeader.TearDown;
105 | begin
106 | inherited;
107 | FHeaders.Free;
108 | end;
109 |
110 | end.
111 |
--------------------------------------------------------------------------------
/tests/src/Test.Action.View.pas:
--------------------------------------------------------------------------------
1 | unit Test.Action.View;
2 |
3 | interface
4 |
5 | uses
6 | TestFrameWork, Notify;
7 |
8 | type
9 | TTestActionView = class(TTestCase)
10 | private
11 | FActionType: TNotifyActionType;
12 | FLabel: String;
13 | FUrl: String;
14 | procedure CallBack(AEvent: INotifyEvent);
15 | public
16 | procedure SetUp; override;
17 | published
18 | procedure Publish;
19 | procedure Subscribe;
20 | end;
21 |
22 | implementation
23 |
24 | uses
25 | System.SysUtils, Test.Constants;
26 |
27 | var
28 | Id, Time: String;
29 |
30 | { TTestActionView }
31 |
32 | procedure TTestActionView.CallBack(AEvent: INotifyEvent);
33 | begin
34 | FLabel := AEvent.Actions.Items[ACTION_LABEL].&Label;
35 | FUrl := AEvent.Actions.Items[ACTION_LABEL].Url;
36 | FActionType := AEvent.Actions.Items[ACTION_LABEL].&Type;
37 | end;
38 |
39 | procedure TTestActionView.Publish;
40 | begin
41 | Ntfy := New.Notify;
42 | Ntfy.Notification(
43 | New.Notification
44 | .Topic(TOPIC)
45 | .Action(New.Action
46 | .&Type(TNotifyActionType.VIEW)
47 | .&Label(ACTION_LABEL)
48 | .Url(ACTION_URL)
49 | ));
50 |
51 | try
52 | try
53 | WriteLn('Send action view: Publish');
54 | Sleep(TIME_DELAY);
55 | Ntfy.ClearFilters;
56 | Ntfy.BaseURL('http://localhost:80');
57 | Ntfy.Publish;
58 | Id := Ntfy.Response.Data.Id;
59 | Time := Ntfy.Response.Data.Time.ToString;
60 | except on E: Exception do
61 | WriteLn(E.Message);
62 | end;
63 | finally
64 | CheckEquals(
65 | Ord(TStatusCode.OK),
66 | Ntfy.Response.StatusCode,
67 | MSG_REQUEST_FAILED
68 | );
69 | end;
70 | end;
71 |
72 | procedure TTestActionView.SetUp;
73 | begin
74 | inherited;
75 | end;
76 |
77 | procedure TTestActionView.Subscribe;
78 | begin
79 | try
80 | try
81 | WriteLn('Send action view: Subscribe');
82 | Sleep(TIME_DELAY);
83 | Ntfy := New.Notify;
84 | Ntfy.BaseURL('http://localhost:80');
85 | Ntfy.Poll(True);
86 | Ntfy.Since(Time);
87 | Ntfy.Subscribe(TOPIC, CallBack);
88 | finally
89 | Ntfy.Unsubscribe;
90 | end;
91 | finally
92 | CheckEquals(Ord(FActionType), Ord(TNotifyActionType.VIEW), MSG_WRONG_HEADERS);
93 | CheckEquals(FLabel, ACTION_LABEL, MSG_WRONG_LABELS);
94 | CheckEquals(FUrl, ACTION_URL, MSG_WRONG_URLS);
95 | end;
96 | end;
97 |
98 | end.
99 |
--------------------------------------------------------------------------------
/tests/src/Test.Attachments.pas:
--------------------------------------------------------------------------------
1 | unit Test.Attachments;
2 |
3 | interface
4 |
5 | uses
6 | TestFramework, Notify;
7 |
8 | type
9 | TTestAttachments = class(TTestCase)
10 | private
11 | FFileName: String;
12 | FFilePath: String;
13 | procedure CallBack(AEvent: INotifyEvent);
14 | public
15 | procedure SetUp; override;
16 | published
17 | procedure Publish;
18 | procedure Subscribe;
19 | end;
20 |
21 | implementation
22 |
23 | uses
24 | Test.Constants, System.SysUtils;
25 |
26 | var
27 | Id, Time: String;
28 |
29 | { TTestAttachments }
30 |
31 | procedure TTestAttachments.CallBack(AEvent: INotifyEvent);
32 | begin
33 | CheckEquals(FFileName, AEvent.Attachment.Name, MSG_WRONG_FILENAME);
34 | end;
35 |
36 | procedure TTestAttachments.Publish;
37 | begin
38 | WriteLn('Send Attachment: Publish');
39 | Ntfy := New.Notify;
40 | Ntfy.Notification(
41 | New.Notification
42 | .Topic(TOPIC)
43 | .AttachFile(FFilePath)
44 | );
45 |
46 | try
47 | try
48 | Sleep(TIME_DELAY);
49 | Ntfy.BaseURL('http://localhost:80');
50 | Ntfy.ClearFilters;
51 | Ntfy.Publish;
52 | Id := Ntfy.Response.Data.Id;
53 | Time := Ntfy.Response.Data.Time.ToString;
54 | except on E: Exception do
55 | WriteLn(E.Message);
56 | end;
57 | finally
58 | CheckEquals(
59 | Ord(TStatusCode.OK),
60 | Ntfy.Response.StatusCode,
61 | MSG_REQUEST_FAILED
62 | );
63 | end;
64 | end;
65 |
66 | procedure TTestAttachments.SetUp;
67 | begin
68 | inherited;
69 | FFilePath := '..\..\img\delphi-notify.png';
70 | FFileName := ExtractFileName(FFilePath);
71 | end;
72 |
73 | procedure TTestAttachments.Subscribe;
74 | begin
75 | WriteLn('Send Attachment: Subscribe');
76 | try
77 | Sleep(TIME_DELAY);
78 | Ntfy := New.Notify;
79 | Ntfy.BaseURL('http://localhost:80');
80 | Ntfy.Poll(True);
81 | Ntfy.Since(Time);
82 | Ntfy.Subscribe(TOPIC, CallBack);
83 | finally
84 | Ntfy.Unsubscribe;
85 | end;
86 | end;
87 |
88 | end.
89 |
--------------------------------------------------------------------------------
/tests/src/Test.Constants.pas:
--------------------------------------------------------------------------------
1 | unit Test.Constants;
2 |
3 | interface
4 |
5 | uses
6 | Notify.Types;
7 |
8 | const TITLE = '⚡ Ntfy for Delphi';
9 | const MESSAGECONTENT = 'A friendly Delphi library to ntfy.sh';
10 | const TOPIC = 'notify-delphi-integration-8jh27d';
11 | const TAGS: array [0..1] of String = ('construction', 'smiley');
12 | const PRIORITY = Ord(TNotifyPriority.MAX);
13 | const ACTION_LABEL = 'Action Label';
14 | const ACTION_URL = 'https://ntfy.sh';
15 | const TIME_DELAY = 2153; //I finished the major test cases in 2022, Dec 31th at 21:53hs
16 | const MSG_WRONG_PRIORITY = 'Sent priority is different from the one received';
17 | const MSG_WRONG_MESSAGE = 'Sent message is different from the one received';
18 | const MSG_WRONG_TITLE = 'Sent title is different from the one received';
19 | const MSG_REQUEST_FAILED = 'Response status code not expected';
20 | const MSG_WRONG_HEADERS = 'Sent headers is different from the one received';
21 | const MSG_WRONG_LABELS = 'Sent label is different from the one received';
22 | const MSG_WRONG_URLS = 'Sent url is different from the one received';
23 | const MSG_WRONG_FILENAME = 'Sent file name is different from the one received';
24 | const MSG_WRONG_EMOJI = 'Sent emojis are different from the ones received';
25 | const MSG_WRONG_ICON = 'Sent icon is different fron the one received';
26 | const MSG_WRONG_URL_ATTACHMENT = 'Sent url attachment is different from the one received';
27 |
28 | {$SCOPEDENUMS ON}
29 | type TStatusCode = (
30 | OK = 200,
31 | SERVER_ERROR = 500,
32 | BAD_REQUEST = 400
33 | );
34 | {$SCOPEDENUMS OFF}
35 |
36 | implementation
37 |
38 | end.
39 |
--------------------------------------------------------------------------------
/tests/src/Test.Email.pas:
--------------------------------------------------------------------------------
1 | unit Test.Email;
2 |
3 | interface
4 |
5 | uses
6 | TestFramework, Notify;
7 |
8 | type
9 | TTestEmail = class(TTestCase)
10 | private
11 | FPriority: TNotifyPriority;
12 | FTitle: String;
13 | FMessageContent: String;
14 | procedure CallBack(AEvent: INotifyEvent);
15 | public
16 | procedure SetUp; override;
17 | published
18 | procedure Publish;
19 | procedure Subscribe;
20 | end;
21 |
22 | implementation
23 |
24 | uses
25 | System.SysUtils, Test.Constants;
26 |
27 | var
28 | Id, Time: String;
29 |
30 | { TTestEmail }
31 |
32 | procedure TTestEmail.CallBack(AEvent: INotifyEvent);
33 | begin
34 | FTitle := AEvent.Title;
35 | FMessageContent := AEvent.MessageContent;
36 | FPriority := AEvent.Priority;
37 | end;
38 |
39 | procedure TTestEmail.Publish;
40 | begin
41 | try
42 | Ntfy := New.Notify;
43 | Ntfy.Notification(
44 | New.Notification
45 | .Topic(TOPIC)
46 | .Title(TITLE)
47 | .MessageContent(MESSAGECONTENT)
48 | .Priority(TNotifyPriority.HIGH)
49 | .Email('hazzelnuts.contact@gmail.com')
50 | );
51 |
52 | try
53 | WriteLn('Send email: Publish');
54 | Sleep(TIME_DELAY);
55 | Ntfy.BaseURL('http://localhost:80');
56 | Ntfy.ClearFilters;
57 | Ntfy.Publish;
58 | Id := Ntfy.Response.Data.Id;
59 | Time := Ntfy.Response.Data.Time.ToString;
60 | except on E: Exception do
61 | Writeln(E.Message)
62 | end;
63 | finally
64 | CheckEquals(
65 | Ord(TStatusCode.OK),
66 | Ntfy.Response.StatusCode,
67 | MSG_REQUEST_FAILED
68 | );
69 | end;
70 | end;
71 |
72 | procedure TTestEmail.SetUp;
73 | begin
74 | inherited;
75 |
76 | end;
77 |
78 | procedure TTestEmail.Subscribe;
79 | begin
80 | try
81 | try
82 | WriteLn('Send email: Subscribe');
83 | Sleep(TIME_DELAY);
84 | Ntfy := New.Notify;
85 | Ntfy.BaseURL('http://localhost:80');
86 | Ntfy.Poll(True);
87 | Ntfy.Since(Time);
88 | Ntfy.Subscribe(Topic, CallBack);
89 | finally
90 | Ntfy.Unsubscribe;
91 | end;
92 | finally
93 | CheckEquals(TITLE, FTitle, MSG_WRONG_TITLE);
94 | CheckEquals(MESSAGECONTENT, FMessageContent, MSG_WRONG_MESSAGE);
95 | CheckEquals(Ord(TNotifyPriority.HIGH), Ord(FPriority), MSG_WRONG_PRIORITY);
96 | end;
97 | end;
98 |
99 | end.
100 |
--------------------------------------------------------------------------------
/tests/src/Test.Emojis.pas:
--------------------------------------------------------------------------------
1 | unit Test.Emojis;
2 |
3 | interface
4 |
5 | uses
6 | TestFrameWork, Notify;
7 |
8 | type
9 | TTestEmojis = class(TTestCase)
10 | private
11 | FEmojis: TArray;
12 | procedure CallBack(AEvent: INotifyEvent);
13 | public
14 | procedure SetUp; override;
15 | published
16 | procedure Publish;
17 | procedure Subscribe;
18 | end;
19 |
20 | implementation
21 |
22 | uses
23 | System.SysUtils, Test.Constants;
24 |
25 | var
26 | Id, Time: String;
27 |
28 | { TTestEmojis }
29 |
30 | procedure TTestEmojis.CallBack(AEvent: INotifyEvent);
31 | var
32 | Idx: Integer;
33 | begin
34 | for Idx := 0 to Length(AEvent.Tags) - 1 do
35 | CheckEquals(FEmojis[Idx], AEvent.Tags[Idx], MSG_WRONG_EMOJI);
36 | end;
37 |
38 | procedure TTestEmojis.Publish;
39 | begin
40 | Ntfy := New.Notify;
41 | Ntfy.Notification(
42 | New.Notification
43 | .Topic(TOPIC)
44 | .Tags(FEmojis)
45 | );
46 |
47 | try
48 | try
49 | WriteLn('Send tags: Publish');
50 | Sleep(TIME_DELAY);
51 | Ntfy.BaseURL('http://localhost:80');
52 | Ntfy.ClearFilters;
53 | Ntfy.Publish;
54 | Id := Ntfy.Response.Data.Id;
55 | Time := Ntfy.Response.Data.Time.ToString;
56 | except on E: Exception do
57 | Writeln(E.Message)
58 | end;
59 | finally
60 | CheckEquals(
61 | Ord(TStatusCode.OK),
62 | Ntfy.Response.StatusCode,
63 | MSG_REQUEST_FAILED
64 | );
65 | end;
66 | end;
67 |
68 | procedure TTestEmojis.SetUp;
69 | begin
70 | inherited;
71 | FEmojis := TArray.Create('warning', 'cd', 'skull');
72 | end;
73 |
74 | procedure TTestEmojis.Subscribe;
75 | begin
76 | try
77 | WriteLn('Send tags: Subscribe');
78 | Sleep(TIME_DELAY);
79 | Ntfy := New.Notify;
80 | Ntfy.BaseURL('http://localhost:80');
81 | Ntfy.Poll(True);
82 | Ntfy.Since(Time);
83 | Ntfy.Subscribe(Topic, CallBack);
84 | finally
85 | Ntfy.Unsubscribe;
86 | end;
87 | end;
88 |
89 | end.
90 |
--------------------------------------------------------------------------------
/tests/src/Test.Icons.pas:
--------------------------------------------------------------------------------
1 | unit Test.Icons;
2 |
3 | interface
4 |
5 | uses
6 | TestFramework, Notify;
7 |
8 | type
9 | TTestIcons = class(TTestCase)
10 | private
11 | FIconURL: String;
12 | const IconURL = 'https://styles.redditmedia.com/t5_32uhe/styles/communityIcon_xnt6chtnr2j21.png';
13 | procedure CallBack(AEvent: INotifyEvent);
14 | public
15 | procedure SetUp; override;
16 | published
17 | procedure Publish;
18 | procedure Subscribe;
19 | end;
20 |
21 | implementation
22 |
23 | uses
24 | System.SysUtils, Test.Constants;
25 |
26 | var
27 | Id, Time: String;
28 |
29 | { TTestIcons }
30 |
31 | procedure TTestIcons.CallBack(AEvent: INotifyEvent);
32 | begin
33 | FIconURL := AEvent.Icon;
34 | end;
35 |
36 | procedure TTestIcons.Publish;
37 | begin
38 |
39 | Ntfy := New.Notify;
40 | Ntfy.Notification(
41 | New.Notification
42 | .Topic(TOPIC)
43 | .Icon(IconURL)
44 | );
45 |
46 | try
47 | try
48 | WriteLn('Send icons: Publish');
49 | Sleep(TIME_DELAY);
50 | Ntfy.BaseURL('http://localhost:80');
51 | Ntfy.ClearFilters;
52 | Ntfy.Publish;
53 | Id := Ntfy.Response.Data.Id;
54 | Time := Ntfy.Response.Data.Time.ToString;
55 | except on E: Exception do
56 | Writeln(E.Message)
57 | end;
58 | finally
59 | CheckEquals(
60 | Ord(TStatusCode.OK),
61 | Ntfy.Response.StatusCode,
62 | MSG_REQUEST_FAILED
63 | );
64 | end;
65 | end;
66 |
67 | procedure TTestIcons.SetUp;
68 | begin
69 | inherited;
70 | end;
71 |
72 | procedure TTestIcons.Subscribe;
73 | begin
74 | try
75 | try
76 | WriteLn('Send icons: Subscribe');
77 | Sleep(TIME_DELAY);
78 | Ntfy := New.Notify;
79 | Ntfy.BaseURL('http://localhost:80');
80 | Ntfy.Poll(True);
81 | Ntfy.Since(Time);
82 | Ntfy.Subscribe(Topic, CallBack);
83 | finally
84 | Ntfy.Unsubscribe;
85 | end;
86 | finally
87 | CheckEquals(FIconURL, IconURL, MSG_WRONG_ICON);
88 | end;
89 | end;
90 |
91 | end.
92 |
--------------------------------------------------------------------------------
/tests/src/Test.Simple.Message.pas:
--------------------------------------------------------------------------------
1 | unit Test.Simple.Message;
2 |
3 | interface
4 |
5 | uses
6 | Notify, TestFramework;
7 |
8 | type
9 | TTestSimpleMessage = class(TTestCase)
10 | private
11 | FTitle: String;
12 | FMessage: String;
13 | FPriority: TNotifyPriority;
14 | procedure CallBack(AEvent: INotifyEvent);
15 | public
16 | procedure SetUp; override;
17 | published
18 | procedure Publish;
19 | procedure Subscribe;
20 | end;
21 |
22 | implementation
23 |
24 | uses
25 | Test.Constants, Winapi.Windows, System.SysUtils;
26 |
27 | var
28 | Id, Time: String;
29 |
30 | { TTestSimpleMessage }
31 |
32 | procedure TTestSimpleMessage.CallBack(AEvent: INotifyEvent);
33 | begin
34 | FTitle := AEvent.Title;
35 | FMessage := AEvent.MessageContent;
36 | FPriority := AEvent.Priority;
37 | end;
38 |
39 | procedure TTestSimpleMessage.Publish;
40 | begin
41 |
42 | Ntfy := New.Notify;
43 | Ntfy.Notification(
44 | New.Notification
45 | .Topic(TOPIC)
46 | .Title(TITLE)
47 | .MessageContent(MESSAGECONTENT)
48 | .Priority(TNotifyPriority.MAX)
49 | );
50 |
51 | try
52 | try
53 | WriteLn('Simple message test: Publish');
54 | Sleep(TIME_DELAY);
55 | Ntfy.BaseURL('http://localhost:80');
56 | Ntfy.ClearFilters;
57 | Ntfy.Publish;
58 | Id := Ntfy.Response.Data.Id;
59 | Time := Ntfy.Response.Data.Time.ToString;
60 | except on E: Exception do
61 | Writeln(E.Message)
62 | end;
63 | finally
64 | CheckEquals(
65 | Ord(TStatusCode.OK),
66 | Ntfy.Response.StatusCode,
67 | MSG_REQUEST_FAILED
68 | );
69 | end;
70 | end;
71 |
72 | procedure TTestSimpleMessage.SetUp;
73 | begin
74 | inherited;
75 |
76 | end;
77 |
78 | procedure TTestSimpleMessage.Subscribe;
79 | begin
80 | try
81 | try
82 | WriteLn('Simple message test: Subscribe');
83 | Sleep(TIME_DELAY);
84 | Ntfy := New.Notify;
85 | Ntfy.BaseURL('http://localhost:80');
86 | Ntfy.Poll(True);
87 | Ntfy.Since(Time);
88 | Ntfy.Subscribe(Topic, CallBack);
89 | finally
90 | Ntfy.Unsubscribe;
91 | end;
92 | finally
93 | CheckEquals(TITLE, FTitle, MSG_WRONG_TITLE);
94 | CheckEquals(MESSAGECONTENT, FMessage, MSG_WRONG_MESSAGE);
95 | CheckEquals(Ord(PRIORITY), Ord(FPriority), MSG_WRONG_PRIORITY);
96 | end;
97 | end;
98 |
99 | end.
100 |
--------------------------------------------------------------------------------
/tests/src/Test.Subscription.pas:
--------------------------------------------------------------------------------
1 | unit Test.Subscription;
2 |
3 | interface
4 |
5 | uses
6 | TestFramework, Notify, System.SyncObjs;
7 |
8 | type
9 |
10 | TMessageStatus = record
11 | received: Boolean;
12 | msg: string;
13 | constructor Create(AReceived: Boolean; AMsg: String);
14 | end;
15 |
16 | TMsgs = class
17 | public
18 | class var
19 | msg1m: TMessageStatus;
20 | msg2m: TMessageStatus;
21 | msg5m: TMessageStatus;
22 | msg10m: TMessageStatus;
23 | msg15m: TMessageStatus;
24 | msg30m: TMessageStatus;
25 | msg1h: TMessageStatus;
26 | msg3h: TMessageStatus;
27 | msg6h: TMessageStatus;
28 | msg12h: TMessageStatus;
29 | msg24h: TMessageStatus;
30 | class procedure Initialize; static;
31 | end;
32 |
33 | TTestSubscription = class(TTestCase)
34 | private
35 | Event: TEvent;
36 | FMsg: TMsgs;
37 | procedure CallBack(AEvent: INotifyEvent);
38 | published
39 | procedure Publish;
40 | procedure Subscribe;
41 | public
42 | constructor Create;
43 | destructor Destroy;
44 | end;
45 |
46 | implementation
47 |
48 | uses
49 | System.SysUtils, Test.Constants, System.Classes;
50 |
51 | { TTestSubscription }
52 |
53 | var
54 | Id, Time: String;
55 |
56 |
57 | procedure TTestSubscription.CallBack(AEvent: INotifyEvent);
58 | begin
59 | WriteLn(AEvent.Title);
60 | end;
61 |
62 | constructor TTestSubscription.Create;
63 | begin
64 | FMsg := TMsgs.Create;
65 | end;
66 |
67 | destructor TTestSubscription.Destroy;
68 | begin
69 | FMsg.Free;
70 | end;
71 |
72 | procedure TTestSubscription.Publish;
73 | var
74 | Id: String;
75 | Time: String;
76 | begin
77 |
78 | try
79 | try
80 |
81 | WriteLn('Long subscription, publishing messages for 1m, 2m, 5m, 10m, 15m, 30m, 1h, 3h, 6h, 12h and 24h');
82 | Sleep(TIME_DELAY);
83 |
84 | Ntfy := New.Notify;
85 | Ntfy.BaseURL('http://localhost:80');
86 | FMsg.Initialize;
87 |
88 | Ntfy.ClearFilters.Notification(
89 | New.Notification
90 | .Topic(TOPIC)
91 | .Delay(FMsg.msg1m.msg)
92 | .Title(FMsg.msg1m.msg)
93 | ).Publish;
94 |
95 | Ntfy.ClearFilters.Notification(
96 | New.Notification
97 | .Topic(TOPIC)
98 | .Delay(FMsg.msg2m.msg)
99 | .Title(FMsg.msg2m.msg)
100 | ).Publish;
101 |
102 | Ntfy.ClearFilters.Notification(
103 | New.Notification
104 | .Topic(TOPIC)
105 | .Delay(FMsg.msg5m.msg)
106 | .Title(FMsg.msg5m.msg)
107 | ).Publish;
108 |
109 | Ntfy.ClearFilters.Notification(
110 | New.Notification
111 | .Topic(TOPIC)
112 | .Delay(FMsg.msg10m.msg)
113 | .Title(FMsg.msg10m.msg)
114 | ).Publish;
115 |
116 | Ntfy.ClearFilters.Notification(
117 | New.Notification
118 | .Topic(TOPIC)
119 | .Delay(FMsg.msg15m.msg)
120 | .Title(FMsg.msg15m.msg)
121 | ).Publish;
122 |
123 | Ntfy.ClearFilters.Notification(
124 | New.Notification
125 | .Topic(TOPIC)
126 | .Delay(FMsg.msg30m.msg)
127 | .Title(FMsg.msg30m.msg)
128 | ).Publish;
129 |
130 | Ntfy.ClearFilters.Notification(
131 | New.Notification
132 | .Topic(TOPIC)
133 | .Delay(FMsg.msg1h.msg)
134 | .Title(FMsg.msg1h.msg)
135 | ).Publish;
136 |
137 | Ntfy.ClearFilters.Notification(
138 | New.Notification
139 | .Topic(TOPIC)
140 | .Delay(FMsg.msg3h.msg)
141 | .Title(FMsg.msg3h.msg)
142 | ).Publish;
143 |
144 | Ntfy.ClearFilters.Notification(
145 | New.Notification
146 | .Topic(TOPIC)
147 | .Delay(FMsg.msg6h.msg)
148 | .Title(FMsg.msg6h.msg)
149 | ).Publish;
150 |
151 | Ntfy.ClearFilters.Notification(
152 | New.Notification
153 | .Topic(TOPIC)
154 | .Delay(FMsg.msg12h.msg)
155 | .Title(FMsg.msg12h.msg)
156 | ).Publish;
157 |
158 | Ntfy.ClearFilters.Notification(
159 | New.Notification
160 | .Topic(TOPIC)
161 | .Delay(FMsg.msg24h.msg)
162 | .Title(FMsg.msg24h.msg)
163 | ).Publish;
164 |
165 |
166 | except on E: Exception do
167 | Writeln(E.Message)
168 | end;
169 | finally
170 | CheckEquals(
171 | Ord(TStatusCode.OK),
172 | Ntfy.Response.StatusCode,
173 | MSG_REQUEST_FAILED
174 | );
175 | end;
176 | end;
177 |
178 | procedure TTestSubscription.Subscribe;
179 | begin
180 | try
181 | WriteLn('Long subscription, waiting for scheduled notifications...');
182 | Sleep(TIME_DELAY);
183 | Ntfy := New.Notify;
184 | Ntfy.BaseURL('http://localhost:80').Subscribe(Topic, CallBack);
185 | finally
186 | Ntfy.Unsubscribe;
187 | end;
188 | end;
189 |
190 | { TMsgs }
191 |
192 | class procedure TMsgs.Initialize;
193 | begin
194 | msg1m := TMessageStatus.Create(False, '1m');
195 | msg2m := TMessageStatus.Create(False, '2m');
196 | msg5m := TMessageStatus.Create(False, '5m');
197 | msg10m := TMessageStatus.Create(False, '10m');
198 | msg15m := TMessageStatus.Create(False, '15m');
199 | msg30m := TMessageStatus.Create(False, '30m');
200 | msg1h := TMessageStatus.Create(False, '1h');
201 | msg3h := TMessageStatus.Create(False, '3h');
202 | msg6h := TMessageStatus.Create(False, '6h');
203 | msg12h := TMessageStatus.Create(False, '12h');
204 | msg24h := TMessageStatus.Create(False, '24h');
205 | end;
206 |
207 | { TMessageStatus }
208 |
209 | constructor TMessageStatus.Create(AReceived: Boolean; AMsg: String);
210 | begin
211 | msg := AMsg;
212 | received := AReceived;
213 | end;
214 |
215 | end.
216 |
--------------------------------------------------------------------------------
/tests/src/Test.URL.Attachments.pas:
--------------------------------------------------------------------------------
1 | unit Test.URL.Attachments;
2 |
3 | interface
4 |
5 | uses
6 | TestFramework, Notify;
7 |
8 | type
9 | TTestURLAttachments = class(TTestCase)
10 | private
11 | FURLAttachment: String;
12 | const URLAttachment =
13 | 'https://images.unsplash.com/photo-1670531910262-'+
14 | '5ddb5ad666ea?crop=entropy&cs=tinysrgb&fit=crop&f'+
15 | 'm=jpg&h=150&ixid=MnwxfDB8MXxyYW5kb218MHx8fHx8fHx'+
16 | '8MTY3MTE0OTI2NA&ixlib=rb-4.0.3&q=80&w=500';
17 | procedure CallBack(AEvent: INotifyEvent);
18 | published
19 | procedure Publish;
20 | procedure Subscribe;
21 | end;
22 |
23 |
24 | implementation
25 |
26 | uses
27 | System.SysUtils, Test.Constants;
28 |
29 | var
30 | Id, Time: String;
31 |
32 | { TTestURLAttachments }
33 |
34 | procedure TTestURLAttachments.CallBack(AEvent: INotifyEvent);
35 | begin
36 | FURLAttachment := AEvent.Attachment.Url;
37 | end;
38 |
39 | procedure TTestURLAttachments.Publish;
40 | begin
41 | Ntfy := New.Notify;
42 | Ntfy.Notification(
43 | New.Notification
44 | .Topic(TOPIC)
45 | .AttachURL(URLAttachment)
46 | );
47 |
48 | try
49 | try
50 | WriteLn('Send url attachments: Publish');
51 | Sleep(TIME_DELAY);
52 | Ntfy.BaseURL('http://localhost:80');
53 | Ntfy.ClearFilters;
54 | Ntfy.Publish;
55 | Id := Ntfy.Response.Data.Id;
56 | Time := Ntfy.Response.Data.Time.ToString;
57 | except on E: Exception do
58 | Writeln(E.Message)
59 | end;
60 | finally
61 | CheckEquals(
62 | Ord(TStatusCode.OK),
63 | Ntfy.Response.StatusCode,
64 | MSG_REQUEST_FAILED
65 | );
66 | end;
67 | end;
68 |
69 | procedure TTestURLAttachments.Subscribe;
70 | begin
71 | try
72 | try
73 | WriteLn('Send url attachments: Subscribe');
74 | Sleep(TIME_DELAY);
75 | Ntfy := New.Notify;
76 | Ntfy.BaseURL('http://localhost:80');
77 | Ntfy.Poll(True);
78 | Ntfy.Since(Time);
79 | Ntfy.Subscribe(Topic, CallBack);
80 | finally
81 | Ntfy.Unsubscribe;
82 | end;
83 | finally
84 | CheckEquals(FURLAttachment, URLAttachment, MSG_WRONG_URL_ATTACHMENT);
85 | end;
86 | end;
87 |
88 | end.
89 |
--------------------------------------------------------------------------------