Skip to content

Commit

Permalink
implement postback
Browse files Browse the repository at this point in the history
  • Loading branch information
j03-dev committed Oct 13, 2023
1 parent dad1fe4 commit 9e182a4
Show file tree
Hide file tree
Showing 9 changed files with 151 additions and 65 deletions.
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@ serde = { version = "1.0", features = ["derive"] }
dotenv = "0.15.0"
sqlx = { version = "0.7.1", features = ["runtime-tokio-rustls", "postgres", "macros"] }
lazy_static = "1.4.0"
url = "2.4.1"
52 changes: 34 additions & 18 deletions src/core/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ pub mod app_state;
use crate::core::app_state::AppState;
use crate::hooks::messages::MsgFromFb;
use crate::hooks::MessengerWebhookRequest;
use crate::response_models::payload::Payload;
use crate::response_models::text::TextModel;
use crate::response_models::SendResponse;
use action::ACTION_REGISTRY;

use rocket::serde::json::Json;
Expand All @@ -26,30 +29,43 @@ pub async fn webhook_verify(request: MessengerWebhookRequest) -> String {

#[post("/webhook", format = "json", data = "<data>")]
pub async fn webhook_core(data: Json<MsgFromFb>, state: &State<AppState>) -> &'static str {
let user_id = &data.get_sender();
let user_id = data.get_sender();
let user_conn = &state.user_conn;
let action = user_conn
.get_action(user_id)
.await
.expect("failed to get action");

if action.ne("lock") {
if let Some(action_fn) = ACTION_REGISTRY.lock().await.get(action.as_str()) {
if let Some(message) = data.get_message() {
user_conn.create(user_id).await;
let text = &message.text.clone();

if let Some(message) = data.get_message() {
let action = user_conn
.get_action(user_id)
.await
.expect("failed to get action");
if action.ne("lock") {
if let Some(action_fn) = ACTION_REGISTRY.lock().await.get(action.as_str()) {
user_conn.set_action(user_id, "lock").await;
action_fn.execute(user_id, text, user_conn).await;
} else if let Some(postback) = data.get_postback() {
let payload = postback.payload.clone();
action_fn
.execute(user_id, message.get_text(), user_conn)
.await;
}
} else {
user_conn.reset_action(user_id).await;
}
} else if let Some(postback) = data.get_postback() {
let uri_payload = postback.get_payload();

action_fn.execute(user_id, &payload, user_conn).await;
match Payload::from_uri_string(uri_payload) {
Ok(payload) => {
if let Some(action_fn) = ACTION_REGISTRY
.lock()
.await
.get(payload.get_action().as_str())
{
action_fn
.execute(user_id, payload.get_value(), user_conn)
.await;
}
}
Err(err) => {
TextModel::new(user_id, &err).send().await.unwrap();
}
}
} else {
user_conn.reset_action(user_id).await;
}

"Ok"
}
43 changes: 29 additions & 14 deletions src/hooks/messages.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use rocket::serde::Deserialize;

#[derive(Debug, Default, Deserialize)]
pub struct Sender {
pub id: String,
id: String,
}

#[derive(Debug, Default, Deserialize)]
Expand All @@ -12,43 +12,58 @@ pub struct Recipient {

#[derive(Debug, Clone, Default, Deserialize)]
pub struct Message {
pub text: String
text: String,
}

impl Message {
pub fn get_text(&self) -> &String {
&self.text
}
}

#[derive(Debug, Clone, Default, Deserialize)]
pub struct Postback {
pub title: String,
pub payload: String,
title: String,
payload: String,
}

impl Postback {
pub fn get_title(&self) -> &String {
&self.title
}

pub fn get_payload(&self) -> &String {
&self.payload
}
}

#[derive(Debug, Default, Deserialize)]
pub struct Messaging {
pub sender: Sender,
pub postback: Option<Postback>,
pub message: Option<Message>
sender: Sender,
postback: Option<Postback>,
message: Option<Message>,
}

#[derive(Debug, Deserialize)]
pub struct Entry {
pub messaging: Vec<Messaging>,
messaging: Vec<Messaging>,
}

#[derive(Debug, Deserialize)]
pub struct MsgFromFb {
pub entry: Vec<Entry>,
entry: Vec<Entry>,
}

impl MsgFromFb {
pub fn get_sender(&self) -> String {
self.entry[0].messaging[0].sender.id.clone()
pub fn get_sender(&self) -> &String {
&self.entry[0].messaging[0].sender.id
}

pub fn get_message(&self) -> Option<Message> {
self.entry[0].messaging[0].message.clone()
self.entry[0].messaging[0].message.clone()
}

pub fn get_postback(&self) -> Option<Postback> {
self.entry[0].messaging[0].postback.clone()
self.entry[0].messaging[0].postback.clone()
}
}

12 changes: 0 additions & 12 deletions src/models/mod.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use std::env::var;

use dotenv::dotenv;
use rocket::tokio;
use sqlx::postgres::PgPoolOptions;
use sqlx::{Pool, Postgres, Row};

Expand Down Expand Up @@ -141,14 +140,3 @@ impl User {
}
}
}

#[tokio::test]
async fn test() {
use self::User;
let user = User::new().await;
// println!("test_migration {}", user.migrate().await);
println!(
"get_action {:?}",
user.get_action("5557815397586928").await.unwrap()
);
}
21 changes: 12 additions & 9 deletions src/response_models/generic.rs
Original file line number Diff line number Diff line change
@@ -1,21 +1,24 @@
use rocket::serde::Serialize;

use super::SendResponse;
use super::{
payload::Payload,
SendResponse,
};

#[derive(Serialize)]
pub struct GenericButton<'b> {
#[serde(rename = "type")]
pub r#type: &'b str,
pub title: String,
pub payload: String,
r#type: &'b str,
title: String,
payload: String,
}

impl<'b> GenericButton<'b> {
pub fn new(title: &str, payload: &str) -> Self {
pub fn new(title: &str, payload: Payload) -> Self {
Self {
r#type: "postback",
title: title.into(),
payload: payload.into(),
payload: payload.to_uri_string(),
}
}
}
Expand All @@ -29,7 +32,7 @@ pub struct GenericElement<'e> {
}

#[derive(Serialize)]
struct Payload<'p> {
struct GenericPayload<'p> {
pub template_type: &'p str,
pub elements: &'p Vec<GenericElement<'p>>,
}
Expand All @@ -38,7 +41,7 @@ struct Payload<'p> {
struct Attachment<'a> {
#[serde(rename = "type")]
pub r#type: &'a str,
pub payload: Payload<'a>,
pub payload: GenericPayload<'a>,
}

#[derive(Serialize)]
Expand All @@ -64,7 +67,7 @@ impl<'g> GenericModel<'g> {
message: GenericMessage {
attachment: Attachment {
r#type: "template",
payload: Payload {
payload: GenericPayload {
template_type: "generic",
elements,
},
Expand Down
1 change: 1 addition & 0 deletions src/response_models/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use dotenv::dotenv;
use rocket::serde::Serialize;
use std::env;

pub mod payload;
pub mod generic;
pub mod media;
pub mod quick_replies;
Expand Down
62 changes: 62 additions & 0 deletions src/response_models/payload.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
use url::form_urlencoded;

#[derive(Debug, Clone, PartialEq)]
pub struct Payload {
action: String,
key: String,
value: String,
}

impl Payload {
pub fn new(action: &str, key: &str, value: &str) -> Self {
Self {
action: action.into(),
key: key.into(),
value: value.into(),
}
}

pub fn get_action(&self) -> &String {
&self.action
}

pub fn get_key(&self) -> &String {
&self.key
}

pub fn get_value(&self) -> &String {
&self.value
}

pub fn to_uri_string(&self) -> String {
form_urlencoded::Serializer::new(String::new())
.append_pair("action", self.get_action())
.append_pair("key", self.get_key())
.append_pair("value", self.get_value())
.finish()
}

pub fn from_uri_string(uri: &str) -> Result<Self, String> {
let parsed: Vec<(String, String)> = form_urlencoded::parse(uri.as_bytes())
.into_owned()
.collect();

let mut action = None;
let mut key = None;
let mut value = None;

for (name, val) in parsed {
match &name[..] {
"action" => action = Some(val),
"key" => key = Some(val),
"value" => value = Some(val),
_ => return Err(format!("Unknown field in URI: {}", name)),
}
}

match (action, key, value) {
(Some(action), Some(key), Some(value)) => Ok(Self::new(&action, &key, &value)),
_ => Err("Missing fields in URI".to_string()),
}
}
}
18 changes: 9 additions & 9 deletions src/response_models/quick_replies.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,10 @@ use super::{generic::Recipient, SendResponse};

#[derive(Serialize)]
pub struct QuickReplie<'r> {
pub content_type: &'r str,
pub title: String,
pub payload: &'r str,
pub image_url: String,
content_type: &'r str,
title: String,
payload: &'r str,
image_url: String,
}

impl<'r> QuickReplie<'r> {
Expand All @@ -23,19 +23,19 @@ impl<'r> QuickReplie<'r> {

#[derive(Serialize)]
struct QuickMessage<'m> {
pub text: String,
pub quick_replies: &'m Vec<QuickReplie<'m>>,
text: String,
quick_replies: &'m Vec<QuickReplie<'m>>,
}

#[derive(Serialize)]
pub struct QuickReplieModel<'q> {
pub recipient: Recipient<'q>,
pub messaging_type: String,
recipient: Recipient<'q>,
messaging_type: String,
message: QuickMessage<'q>,
}

impl<'q> QuickReplieModel<'q> {
pub fn new(sender: &'q str, message: &str, quick_replies: &'q Vec<QuickReplie>) -> Self {
pub fn new(sender: &'q str, message: &str, quick_replies: &'q Vec<QuickReplie>) -> Self {
Self {
recipient: Recipient { id: sender },
messaging_type: "RESPONSE".into(),
Expand Down
6 changes: 3 additions & 3 deletions src/response_models/text.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,13 @@ use super::{generic::Recipient, SendResponse};

#[derive(Serialize)]
pub struct Text<'t> {
pub text: &'t str,
text: &'t str,
}

#[derive(Serialize)]
pub struct TextModel<'s> {
pub recipient: Recipient<'s>,
pub message: Text<'s>,
recipient: Recipient<'s>,
message: Text<'s>,
}

impl<'s> TextModel<'s> {
Expand Down

0 comments on commit 9e182a4

Please sign in to comment.