Skip to content

Commit d5c320e

Browse files
committed
added motre details and config examples for listmonk implementation #7
1 parent 4392ed5 commit d5c320e

6 files changed

Lines changed: 176 additions & 9 deletions

File tree

README.md

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,82 @@ All options are described in the example file.
139139
After starting the API the first time it will add additional fields to each event.
140140
Such as a choice for the hosting Kennel or amount of Hash Cash.
141141

142+
## Listmonk support to post event
143+
It is possible to post an event directly to via [Listmonk](https://github.com/knadh/listmonk) to a mailing list.
144+
145+
### Requirements
146+
* a running listmonk instance
147+
* WordPress Advanced Custom Fields plugin
148+
* Wordpress WPCode Plugin
149+
150+
First both WordPress plugins should be installed.
151+
152+
Using the `Advanced Custom Fields` we add a new Field Group `Run Announcement`. There we add a new field `Mailing List`.
153+
* Type: Message
154+
* Label: Mailing List
155+
* Name: mailing_list
156+
* Message:
157+
```
158+
<div>
159+
<button
160+
id="send-to-mailing-list-button"
161+
type="button"
162+
class="button-secondary send-to-mailing-list-button"
163+
id="refresh-cache">Send/Update Event to Mailing List
164+
</button>
165+
</div>
166+
```
167+
168+
In the bottom we can find `Settings`.
169+
* Rules:
170+
1. Post Typ
171+
2. is equal to
172+
3. Event
173+
* Representation:
174+
1. Style: Standard
175+
2. Position: Side
176+
3. Label Placement: Top
177+
4. Instruction placement: Below
178+
5. Order No: 2
179+
180+
* Now press `Save Changes`
181+
182+
After that we switch to the `WPCode` plugin and a Javascript function to this button we just created.
183+
Here we add a new `Send Mailinglist` code snippet of type `PHP Snipet` with this content:
184+
```php
185+
/* Inline script printed out in the header */
186+
add_action('admin_footer', 'tutsplus_add_script_wp_head');
187+
function tutsplus_add_script_wp_head() {
188+
?>
189+
<script id="updateMailingList" type="text/javascript">
190+
document.querySelector( '.send-to-mailing-list-button' ).addEventListener( 'click', function( e ) {
191+
var xhttp = new XMLHttpRequest();
192+
var params = {
193+
user: "<?php echo get_current_user_id(); ?>",
194+
token: "<?php echo wp_get_session_token(); ?>"
195+
}
196+
197+
xhttp.onreadystatechange = function() {
198+
if (this.readyState == 4 && this.status == 200) {
199+
console.log(this.responseText);
200+
}
201+
};
202+
203+
xhttp.open("POST", "/api/v1/send-newsletter/<?php echo get_the_ID(); ?>" , true);
204+
xhttp.setRequestHeader('Content-type', 'application/json')
205+
206+
xhttp.send(JSON.stringify(params));
207+
xhttp.onload = function() {
208+
// Do whatever with response
209+
alert("Mailing List request status: " + xhttp.responseText)
210+
}
211+
212+
} );
213+
</script>
214+
<?php
215+
}
216+
```
217+
With Listmonk running on the same hos, sending an event via Mailing list is just a button press away.
142218

143219
## License
144220
>You can check out the full license [here](LICENSE.txt)

api/models/send_newsletter.py

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,50 @@
88
# repository or visit: <https://opensource.org/licenses/MIT>.
99

1010
from pydantic.dataclasses import dataclass
11+
from typing import Any, Dict, List
12+
from pydantic import BaseModel
1113

1214

1315
@dataclass
1416
class SendNewsletterParams:
15-
1617
user: int
1718
token: str
19+
20+
21+
class MailingListModel(BaseModel):
22+
id: int
23+
name: str
24+
25+
26+
class ListmonkReturnData(BaseModel):
27+
id: int
28+
created_at: str
29+
updated_at: str
30+
views: int
31+
clicks: int
32+
bounces: int
33+
lists: List[MailingListModel]
34+
started_at: Any
35+
to_send: int
36+
sent: int
37+
uuid: str
38+
type: str
39+
name: str
40+
subject: str
41+
from_email: str
42+
body: str
43+
altbody: Any
44+
send_at: Any
45+
status: str
46+
content_type: str
47+
tags: List
48+
headers: List
49+
template_id: int
50+
messenger: str
51+
archive: bool
52+
archive_template_id: int
53+
archive_meta: Dict[str, Any]
54+
55+
56+
class ListmonkReturnDataList(BaseModel):
57+
data: ListmonkReturnData

api/routers/send_newsletter.py

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
from time import time
1313

1414
from api.models.run import HashParams
15-
from api.models.send_newsletter import SendNewsletterParams
15+
from api.models.send_newsletter import SendNewsletterParams, ListmonkReturnDataList
1616
from api.models.exceptions import CredentialsInvalid
1717
from api.factory.runs import get_hash_runs
1818
from source.database import get_db_handler
@@ -30,7 +30,8 @@
3030

3131

3232
# noinspection PyShadowingBuiltins
33-
@newsletter.post("/{post_id}", summary="post run", description="post run via newsletter")
33+
@newsletter.post("/{post_id}", response_model=ListmonkReturnDataList, summary="post run",
34+
description="post run via newsletter")
3435
async def get_run(post_id: int, params: SendNewsletterParams):
3536
"""
3637
To post/update a run to listmonk
@@ -127,12 +128,15 @@ async def get_run(post_id: int, params: SendNewsletterParams):
127128
"name": f"{subject_prefix}{event.event_name}",
128129
"subject": f"{subject_prefix}[{event.kennel_name}] Run #{event.run_number}, "
129130
f"{event.start_date:%A %d %B %Y, %H:%M} @ {event.location_name}",
130-
"lists": [listmonk_handler.config.list_id],
131+
"lists": listmonk_handler.config.list_ids,
131132
"type": "regular",
132133
"content_type": "html",
133134
"body": campaign_body
134135
}
135136

137+
if listmonk_handler.config.campaign_template_id is not None:
138+
campaign_data["template_id"] = listmonk_handler.config.campaign_template_id
139+
136140
# create listmonk campaign
137141
campaign_result = listmonk_handler.add_campaign(campaign_data)
138142

@@ -142,17 +146,18 @@ async def get_run(post_id: int, params: SendNewsletterParams):
142146
campaign_id = grab(campaign_result, "data.id")
143147

144148
# send campaign
145-
campaign_running_result = listmonk_handler.set_campaign_status(campaign_id, "running")
149+
if listmonk_handler.config.send_campaign is True:
150+
campaign_result = listmonk_handler.set_campaign_status(campaign_id, "running")
146151

147-
if campaign_running_result is None:
148-
raise HTTPException(status_code=503, detail=f"Upstream request failed, unable to start campaign")
152+
if campaign_result is None:
153+
raise HTTPException(status_code=503, detail=f"Upstream request failed, unable to start campaign")
149154

150155
# write campaign id to WP database
151156
if post_campaign_id is not None:
152157
db_handler.update_post_meta(post_id, "listmonk_campaign_id", campaign_id)
153158
else:
154159
db_handler.add_post_meta(post_id, "listmonk_campaign_id", campaign_id)
155160

156-
return {"status": "ok"}
161+
return ListmonkReturnDataList(**campaign_result)
157162

158163
# EOF

config-example.ini

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,38 @@ hash_kennels = Nerd H3, Nerd Full Moon H3
9494
# add the FaceBook group ID here. This ID will be added to each run.
9595
#default_facebook_group_id =
9696

97+
###
98+
### [listmonk]
99+
###
100+
### settings to connect to listmonk to prepare/send newsletter campaign
101+
###
102+
103+
[listmonk]
104+
105+
# Setting this option to true enables the listmonk integration
106+
#enabled = false
107+
108+
# defines if the campaign should be sent out immediately after it got created
109+
#send_campaign = false
110+
111+
# defines the admin credentials in order to connect to the listmonk API
112+
#username =
113+
#password =
114+
115+
# defines the listmonk host name to connect to
116+
#host = list.example.com
117+
118+
# The id of the campaign template to use, if empty the default template is used
119+
#campaign_template_id =
120+
121+
# The id of the template to use which then is posted as content to the campaign
122+
#body_template_id =
123+
124+
# the id or ids of the list to send this campaign to
125+
#list_ids =
126+
127+
[database]
128+
97129

98130
###
99131
### [database]

config/models/listmonk.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,28 @@
88
# repository or visit: <https://opensource.org/licenses/MIT>.
99

1010
from config.models import EnvOverridesBaseSettings
11+
from pydantic import validator
1112

1213

14+
# noinspection PyMethodParameters
1315
class ListMonkSettings(EnvOverridesBaseSettings):
1416
enabled: bool = False
17+
send_campaign: bool = False
1518
username: str
1619
password: str
1720
host: str
21+
campaign_template_id: int = None
1822
body_template_id: int
19-
list_id: int
23+
list_ids: str
2024

2125
class Config:
2226
env_prefix = f"{__name__.split('.')[-1]}_"
27+
28+
@validator("list_ids")
29+
def split_list_ids(cls, value):
30+
if isinstance(value, str):
31+
try:
32+
value = [int(x) for x in value.split(",")]
33+
except Exception as e:
34+
raise ValueError(f"unable to pars '{value}' into list of ints: {e}")
35+
return value

requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@ pytz==2021.3
77
phpserialize==1.3
88
mysql-connector==2.2.9
99
psutil==5.9.0
10+
requests==2.28.2

0 commit comments

Comments
 (0)