comparison mod_voipms/mod_voipms.lua @ 6300:8689f49d21ef

mod_voipms: Updates
author Menel <menel@snikket.de>
date Tue, 22 Jul 2025 06:24:32 +0200
parents 5cf5ee23b361
children c194bec4b56d
comparison
equal deleted inserted replaced
6299:5cf5ee23b361 6300:8689f49d21ef
9 local rest_endpoint = "https://voip.ms/api/v1/rest.php" 9 local rest_endpoint = "https://voip.ms/api/v1/rest.php"
10 10
11 if not api_username or not api_password or not query_key then 11 if not api_username or not api_password or not query_key then
12 module:log("error", "Missing required config values (voipms_api_username, voipms_api_password, voipms_query_key)") 12 module:log("error", "Missing required config values (voipms_api_username, voipms_api_password, voipms_query_key)")
13 return 13 return
14 end
15
16 for jid, dids in pairs(jid_map) do
17 if type(dids) ~= "table" then
18 module:log("error", "Invalid voipms_jid_map entry for %s: must be a list of DIDs", jid)
19 return
20 end
14 end 21 end
15 22
16 module:depends("http") 23 module:depends("http")
17 24
18 local function normalize_number(num) 25 local function normalize_number(num)
57 if not from or not to then 64 if not from or not to then
58 module:log("warn", "Missing phone numbers (from: %s, to: %s)", tostring(from), tostring(to)) 65 module:log("warn", "Missing phone numbers (from: %s, to: %s)", tostring(from), tostring(to))
59 return { status_code = 400 } 66 return { status_code = 400 }
60 end 67 end
61 68
69 local normalized_from = normalize_number(from)
62 local normalized_to = normalize_number(to) 70 local normalized_to = normalize_number(to)
63 local target_jid = nil 71 local target_jid = nil
64 72
65 for jid, did in pairs(jid_map) do 73 for jid, dids in pairs(jid_map) do
66 if normalize_number(did) == normalized_to then 74 for _, did in ipairs(dids) do
67 target_jid = jid 75 if normalize_number(did) == normalized_to then
68 break 76 target_jid = jid
77 break
78 end
69 end 79 end
80 if target_jid then break end
70 end 81 end
71 82
72 if not target_jid then 83 if not target_jid then
73 module:log("warn", "No JID mapping for DID %s", normalized_to) 84 module:log("warn", "No JID mapping for DID %s", normalized_to)
74 return { status_code = 404 } 85 return { status_code = 404 }
75 end 86 end
76 87
77 local normalized_from = normalize_number(from)
78 local message_text = payload.text or "" 88 local message_text = payload.text or ""
89 local from_jid = normalized_from .. "%" .. normalized_to .. "@" .. module.host
79 90
80 local message = st.message({ 91 if message_text ~= "" then
81 from = normalized_from .. "@" .. module.host, 92 local text_message = st.message({
82 to = target_jid, 93 from = from_jid,
83 type = "chat" 94 to = target_jid,
84 }):tag("body"):text(message_text):up() 95 type = "chat"
96 }):tag("body"):text(message_text):up()
97
98 module:send(text_message)
99 module:log("info", "Delivered text SMS from %s to %s", normalized_from, target_jid)
100 end
85 101
86 if payload.media and #payload.media > 0 then 102 if payload.media and #payload.media > 0 then
87 for _, media_item in ipairs(payload.media) do 103 for _, media_item in ipairs(payload.media) do
88 if media_item.url then 104 if media_item.url then
89 message_text = message_text .. "\n" .. media_item.url 105 local media_message = st.message({
90 message:tag("x", { xmlns = "jabber:x:oob" }) 106 from = from_jid,
91 :tag("url"):text(media_item.url):up():up() 107 to = target_jid,
108 type = "chat"
109 })
110 :tag("body"):text(media_item.url):up()
111 :tag("x", { xmlns = "jabber:x:oob" })
112 :tag("url"):text(media_item.url):up()
113 :up()
114
115 module:send(media_message)
116 module:log("info", "Delivered media URL from %s to %s: %s", normalized_from, target_jid, media_item.url)
92 end 117 end
93 end 118 end
94 end 119 end
95
96 module:send(message)
97 module:log("info", "Delivered SMS from %s to %s", normalized_from, target_jid)
98 120
99 return { status_code = 204 } 121 return { status_code = 204 }
100 end 122 end
101 } 123 }
102 }) 124 })
108 local from_jid = stanza.attr.from 130 local from_jid = stanza.attr.from
109 local to_jid = stanza.attr.to 131 local to_jid = stanza.attr.to
110 local body = stanza:get_child_text("body") 132 local body = stanza:get_child_text("body")
111 if not body or body == "" then return end 133 if not body or body == "" then return end
112 134
113 local from_number = jid_map[from_jid] 135 local to_node = to_jid:match("^(.-)@")
114 if not from_number then 136 if not to_node then
115 module:log("warn", "No DID mapping for JID %s", from_jid)
116 return
117 end
118
119 local to_number = to_jid:match("^([^@]+)")
120 if not to_number then
121 module:log("warn", "Malformed JID in message.to: %s", to_jid) 137 module:log("warn", "Malformed JID in message.to: %s", to_jid)
122 return 138 return
123 end 139 end
124 140
125 to_number = normalize_number(to_number) 141 local dst_number, from_number = to_node:match("([^%%]+)%%(.+)")
142 if not dst_number or not from_number then
143 module:log("warn", "Malformed to JID node: %s", to_node)
144 return
145 end
146
147 dst_number = normalize_number(dst_number)
148 from_number = normalize_number(from_number)
126 149
127 local media_urls = {} 150 local media_urls = {}
128 for line in body:gmatch("[^\r\n]+") do 151 for line in body:gmatch("[^\r\n]+") do
129 if line:match("^https?://") then 152 if line:match("^https?://") then
130 table.insert(media_urls, line) 153 table.insert(media_urls, line)
135 local query = { 158 local query = {
136 api_username = api_username, 159 api_username = api_username,
137 api_password = api_password, 160 api_password = api_password,
138 method = method, 161 method = method,
139 did = from_number, 162 did = from_number,
140 dst = to_number, 163 dst = dst_number,
141 message = body 164 message = body
142 } 165 }
143 166
144 if method == "sendMMS" then 167 if method == "sendMMS" then
145 for i, url in ipairs(media_urls) do 168 for i, url in ipairs(media_urls) do
155 if code == 200 then 178 if code == 200 then
156 local resp, err = json.decode(response_body) 179 local resp, err = json.decode(response_body)
157 if not resp or resp.status ~= "success" then 180 if not resp or resp.status ~= "success" then
158 module:log("error", "Failed to send %s: %s", method, err or (resp and resp.status) or "unknown") 181 module:log("error", "Failed to send %s: %s", method, err or (resp and resp.status) or "unknown")
159 else 182 else
160 module:log("info", "Sent %s from %s to %s", method, from_number, to_number) 183 module:log("info", "Sent %s from %s to %s", method, from_number, dst_number)
161 end 184 end
162 else 185 else
163 module:log("error", "HTTP error sending %s: code %s", method, tostring(code)) 186 module:log("error", "HTTP error sending %s: code %s", method, tostring(code))
164 end 187 end
165 end) 188 end)