- Cho telegram-cli chạy nền
- Chặn sự kiện msg.in để chọn lọc các tin nhắn đến và trả lời dựa trên tập hợp các từ khóa có sẵn
Có vài cách để đọc tin nhắn đến. Ở đây chúng ta dùng ngôn ngữ kịch bản lua. Ngôn ngữ lua hỗ trợ regular expression và csdl kém nên chỉ dùng cách này khi tập hợp từ khóa nhỏ (vài trăm ngàn)
Các từ khóa, thí dụ, dùng để chạy lệnh trên RPi (các lệnh được phép chạy) được ghi trong file command.csv
cd cp df du id ln ls mv rm cat man sed who date echo exit find free grep kill ping read size stat tail chgrp chmod chown mkdir pidof rmdir uname md5sum pstree reboot uptime whatis whoami killall whereis hostname shutdown
Chúng ta không sắp xếp theo tên, mà trước hết sắp xếp theo độ dài. Để biết một lệnh nằm trong tin nhắn có được phép thi hành hay không, chúng ta tra theo danh sách này theo cách tuần tự nếu danh sách nhỏ, hay dùng binary search theo chiều dài từ khóa trước nếu danh sách lớn.
Tương tự, chúng ta tạo danh sách khác dùng như từ điển hay danh sách hàng hóa và giá bán, đặt tên là keyword.csv
"Macbook Pro Retina 13.3inch MF840", "27.999.000" "Macbook Pro Retina 15.4inch MGXA2", "30.339.000" "Macbook Pro Retina 13.3inch MF840", "26.899.000"
Có thể chia thành nhiều cột để dễ nhận diện từ khóa
Tiếp theo, chúng ta tạo lua script tên là action.lua, thay thế cho script xử lý mặc định của telegram
keyfile = '/home/pi/keyword.csv' cmdfile = '/home/pi/command.csv' keywords = {} commands = {} --++ cắt khoảng trắng 2 bên chuỗi ++-- function trim(str) str = tostring(str or '') return str:gsub("^%s*(.-)%s*$", "%1") end --++ đọc các field từ một dòng csv ++-- function csvsplit (str, sep) sep = '%s*'..sep..'%s*' str = trim(str) fields = {} fieldstart = 1 if str then repeat _, i = str:find ('^%s*"', fieldstart) if i then -- field bọc trong "" fieldstart = i repeat -- tìm field i, e = str:find ('"("?)', i+1) until i == e local f = str:sub (fieldstart+1, i-1) table.insert (fields, (f:gsub ('""', '"'))) _, i = str:find (sep, i) if i ~= nil then fieldstart = i+1 else break end else -- field không bọc trong "" local _, i = str:find (sep, fieldstart) if i ~= nil then table.insert (fields, str:sub (fieldstart, i-1)) fieldstart = i + 1 else table.insert (fields, str:sub (fieldstart, str:len())) break end end until fieldstart > str:len() end return fields end --++ đọc csv file vào table ++-- function csvLoad(path, sep) local csvTable = {} local file = assert (io.open(path, "r")) for line in file:lines() do fields = csvsplit (line, sep) if next (fields) ~= nil then table.insert (csvTable, fields) end end file:close() return csvTable end --++ đọc các file csv vào table ++-- keywords = csvLoad (keyfile, ',') commands = csvLoad (cmdfile, ',') --++ hàm xử lý sự kiện tin nhắn đến ++-- function on_msg_receive (msg) vusr = {'Ly_Anh-Tuan, Test_User'} --user có trong danh sách (hợp lệ)?-- function validuser(usr) found = false for i=1,#vusr do if vusr[i] == usr then found = true break end end return found end --chỉ xử lý tin đến và từ user trong danh sách-- if msg.out or not validuser(msg.from.print_name) then return end --------------------------- function iskeyword(str, sFind) str = tostring (str or '') return str:gsub('(.+)', ' %1 '):find('([%s%p])('..sFind..')([%s%p])') end --------------------------- function findkeyword (str) -- binarysearch keywords theo length, rồi theo word -- tìm tuần tự nếu csdl nhỏ str = tostring (str or '') result = '' for i=1,#keywords do local kw = keywords[i][1] local c = kw:sub(1,1) local tmp = '['..string.lower(c)..','..string.upper(c)..']'..kw:sub(2) if iskeyword(str, tmp) ~= nil then result = result..'['..kw.."\n"..keywords[i][2].."]\n" end end return result end --Thực hiện lệnh trên RPi và lấy kết quả trả về function osrun(cmd) local tmp = os.tmpname () os.execute (cmd .. ' > ' .. tmp .. ' 2>&1') local f = assert (io.open (tmp, 'rb')) local result = f:read ('*all') f:close () os.remove (tmp) return tostring (result or '') end ---Tìm từ khóa/lệnh và thực hiện--- local reply = '' local tmp = findkeyword (msg.text) if tmp ~= '' then reply = "###### keywords ######\n" .. tmp .. "\n" end local tmp = findcommand (msg.text) if tmp ~= '' then reply = reply .. "###### commands ######\n" .. tmp .. "\n" end reply = reply .. "###### messages ######\n" .. msg.text ---gởi tin nhắn trả lời--- send_msg (msg.from.print_name, reply, ok_cb, false) end --Các hàm đáp ứng các sự kiện khác của telegram, không cần can thiệp-- --++++++++++++++++++++++++++++++++-- function ok_cb() end --++++++++++++++++++++++++++++++++-- function on_our_id (id) end --++++++++++++++++++++++++++++++++-- function on_secret_chat_created (peer) end --++++++++++++++++++++++++++++++++-- function on_user_update (user) end --++++++++++++++++++++++++++++++++-- function on_chat_update (user) end --++++++++++++++++++++++++++++++++-- function on_get_difference_end () end --++++++++++++++++++++++++++++++++-- function on_binlog_replay_end () end
Cuối cùng, trong dòng lệnh gọi chạy telegram-cli phải có tham số -s /path/to/script.lua
telegram-cli -W -s /home/pi/action.cript
Sau đó các tin nhắn đến đều được xử lý và trả lời, qui ước
- Từ khóa sẽ được tự động dò tìm, không cần đánh dấu
- Lệnh phải đặt trong [lệnh]
Thí dụ về tin trả lời khi gởi tin có nội dung [ls /]
###### commands ###### [ls / bin boot boot.bak dev etc home lib lost+found media mnt opt proc root run sbin srv sys tmp usr var ] ###### messages ###### [ls /]
Chú thích
- Bất cứ ai biết username của mình đều có thể nhắn tin telegram, vì vậy phải có danh sách user có quyền nhắn tin và lọc user trước
- Với lệnh sudo gởi qua telegram, user telegramd phải được thêm vào /etc/sudoers mới có thể thi hành lệnh
- Có thể cho telegram-cli chạy nền khi khởi động để xử lý tin
- telegram-cli phải được gọi sau khi quá trình khởi động hoàn tất
@reboot sleep 30 && telegram-daemon start
- Để có thể dùng Regular Expression một cách linh hoat, dễ dàng hơn, cần phải cài đặt thêm lua-rex-pcre
apt-get install lua-rex-pcre
Khi đó dòng lệnh regular expression tương tự như sau
rex = require "rex_pcre"
print (rex.match ("Hôm nay tôi đi học", "TÔI ĐI HỌC", 1+2048)) --> tôi đi học