mirror of
https://github.com/gSpotx2f/luci-app-internet-detector.git
synced 2025-12-09 21:17:00 +03:00
v1.6. New module: mod_telegram. mod_public_ip: Added support for HTTP services.
This commit is contained in:
42
README.md
42
README.md
@@ -13,26 +13,27 @@ Internet-detector is an application for checking the availability of the Interne
|
||||
**OpenWrt >= 21.02.**
|
||||
|
||||
**Dependences:** lua, luaposix, libuci-lua.
|
||||
**Recommended:** curl.
|
||||
|
||||
## Installation notes:
|
||||
|
||||
opkg update
|
||||
wget --no-check-certificate -O /tmp/internet-detector_1.5.2-r1_all.ipk https://github.com/gSpotx2f/packages-openwrt/raw/master/current/internet-detector_1.5.2-r1_all.ipk
|
||||
opkg install /tmp/internet-detector_1.5.2-r1_all.ipk
|
||||
rm /tmp/internet-detector_1.5.2-r1_all.ipk
|
||||
wget --no-check-certificate -O /tmp/internet-detector_1.6.0-r1_all.ipk https://github.com/gSpotx2f/packages-openwrt/raw/master/current/internet-detector_1.6.0-r1_all.ipk
|
||||
opkg install /tmp/internet-detector_1.6.0-r1_all.ipk
|
||||
rm /tmp/internet-detector_1.6.0-r1_all.ipk
|
||||
service internet-detector start
|
||||
service internet-detector enable
|
||||
|
||||
wget --no-check-certificate -O /tmp/luci-app-internet-detector_1.5.2-r1_all.ipk https://github.com/gSpotx2f/packages-openwrt/raw/master/current/luci-app-internet-detector_1.5.2-r1_all.ipk
|
||||
opkg install /tmp/luci-app-internet-detector_1.5.2-r1_all.ipk
|
||||
rm /tmp/luci-app-internet-detector_1.5.2-r1_all.ipk
|
||||
wget --no-check-certificate -O /tmp/luci-app-internet-detector_1.6.0-r1_all.ipk https://github.com/gSpotx2f/packages-openwrt/raw/master/current/luci-app-internet-detector_1.6.0-r1_all.ipk
|
||||
opkg install /tmp/luci-app-internet-detector_1.6.0-r1_all.ipk
|
||||
rm /tmp/luci-app-internet-detector_1.6.0-r1_all.ipk
|
||||
service rpcd restart
|
||||
|
||||
i18n-ru:
|
||||
|
||||
wget --no-check-certificate -O /tmp/luci-i18n-internet-detector-ru_1.5.2-r1_all.ipk https://github.com/gSpotx2f/packages-openwrt/raw/master/current/luci-i18n-internet-detector-ru_1.5.2-r1_all.ipk
|
||||
opkg install /tmp/luci-i18n-internet-detector-ru_1.5.2-r1_all.ipk
|
||||
rm /tmp/luci-i18n-internet-detector-ru_1.5.2-r1_all.ipk
|
||||
wget --no-check-certificate -O /tmp/luci-i18n-internet-detector-ru_1.6.0-r1_all.ipk https://github.com/gSpotx2f/packages-openwrt/raw/master/current/luci-i18n-internet-detector-ru_1.6.0-r1_all.ipk
|
||||
opkg install /tmp/luci-i18n-internet-detector-ru_1.6.0-r1_all.ipk
|
||||
rm /tmp/luci-i18n-internet-detector-ru_1.6.0-r1_all.ipk
|
||||
|
||||
## Screenshots:
|
||||
|
||||
@@ -44,9 +45,9 @@ i18n-ru:
|
||||
|
||||
**Dependences:** modemmanager.
|
||||
|
||||
wget --no-check-certificate -O /tmp/internet-detector-mod-modem-restart_1.5.2-r1_all.ipk https://github.com/gSpotx2f/packages-openwrt/raw/master/current/internet-detector-mod-modem-restart_1.5.2-r1_all.ipk
|
||||
opkg install /tmp/internet-detector-mod-modem-restart_1.5.2-r1_all.ipk
|
||||
rm /tmp/internet-detector-mod-modem-restart_1.5.2-r1_all.ipk
|
||||
wget --no-check-certificate -O /tmp/internet-detector-mod-modem-restart_1.6.0-r1_all.ipk https://github.com/gSpotx2f/packages-openwrt/raw/master/current/internet-detector-mod-modem-restart_1.6.0-r1_all.ipk
|
||||
opkg install /tmp/internet-detector-mod-modem-restart_1.6.0-r1_all.ipk
|
||||
rm /tmp/internet-detector-mod-modem-restart_1.6.0-r1_all.ipk
|
||||
service internet-detector restart
|
||||
|
||||

|
||||
@@ -55,9 +56,20 @@ i18n-ru:
|
||||
|
||||
**Dependences:** mailsend.
|
||||
|
||||
wget --no-check-certificate -O /tmp/internet-detector-mod-email_1.5.2-r1_all.ipk https://github.com/gSpotx2f/packages-openwrt/raw/master/current/internet-detector-mod-email_1.5.2-r1_all.ipk
|
||||
opkg install /tmp/internet-detector-mod-email_1.5.2-r1_all.ipk
|
||||
rm /tmp/internet-detector-mod-email_1.5.2-r1_all.ipk
|
||||
wget --no-check-certificate -O /tmp/internet-detector-mod-email_1.6.0-r1_all.ipk https://github.com/gSpotx2f/packages-openwrt/raw/master/current/internet-detector-mod-email_1.6.0-r1_all.ipk
|
||||
opkg install /tmp/internet-detector-mod-email_1.6.0-r1_all.ipk
|
||||
rm /tmp/internet-detector-mod-email_1.6.0-r1_all.ipk
|
||||
service internet-detector restart
|
||||
|
||||

|
||||
|
||||
## Telegram notification module (internet-detector-mod-telegram):
|
||||
|
||||
**Dependences:** curl.
|
||||
|
||||
wget --no-check-certificate -O /tmp/internet-detector-mod-telegram_1.6.0-r1_all.ipk https://github.com/gSpotx2f/packages-openwrt/raw/master/current/internet-detector-mod-telegram_1.6.0-r1_all.ipk
|
||||
opkg install /tmp/internet-detector-mod-telegram_1.6.0-r1_all.ipk
|
||||
rm /tmp/internet-detector-mod-telegram_1.6.0-r1_all.ipk
|
||||
service internet-detector restart
|
||||
|
||||

|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=internet-detector-mod-email
|
||||
PKG_VERSION:=1.5.2
|
||||
PKG_VERSION:=1.6.0
|
||||
PKG_RELEASE:=1
|
||||
PKG_MAINTAINER:=gSpot <https://github.com/gSpotx2f/luci-app-internet-detector>
|
||||
|
||||
|
||||
@@ -86,7 +86,7 @@ function Module:init(t)
|
||||
self._enabled = true
|
||||
else
|
||||
self._enabled = false
|
||||
self.syslog("warning", string.format("%s: %s is not available", self.name, self.mta))
|
||||
self.syslog("err", string.format("%s: %s is not available", self.name, self.mta))
|
||||
end
|
||||
|
||||
if (not self.mailRecipient or
|
||||
@@ -162,6 +162,7 @@ function Module:run(currentStatus, lastStatus, timeDiff, timeNow, inetChecked)
|
||||
if not self._enabled then
|
||||
return
|
||||
end
|
||||
|
||||
if currentStatus == 1 then
|
||||
self._aliveCounter = 0
|
||||
self._msgSentConnect = false
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=internet-detector-mod-modem-restart
|
||||
PKG_VERSION:=1.5.2
|
||||
PKG_VERSION:=1.6.0
|
||||
PKG_RELEASE:=1
|
||||
PKG_MAINTAINER:=gSpot <https://github.com/gSpotx2f/luci-app-internet-detector>
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@ function Module:init(t)
|
||||
self._enabled = true
|
||||
else
|
||||
self._enabled = false
|
||||
self.syslog("warning", string.format(
|
||||
self.syslog("err", string.format(
|
||||
"%s: modemmanager service is not available", self.name))
|
||||
end
|
||||
end
|
||||
|
||||
41
internet-detector-mod-telegram/Makefile
Normal file
41
internet-detector-mod-telegram/Makefile
Normal file
@@ -0,0 +1,41 @@
|
||||
#
|
||||
# (с) 2025 gSpot (https://github.com/gSpotx2f/luci-app-internet-detector)
|
||||
#
|
||||
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=internet-detector-mod-telegram
|
||||
PKG_VERSION:=1.6.0
|
||||
PKG_RELEASE:=1
|
||||
PKG_MAINTAINER:=gSpot <https://github.com/gSpotx2f/luci-app-internet-detector>
|
||||
|
||||
include $(INCLUDE_DIR)/package.mk
|
||||
|
||||
define Package/$(PKG_NAME)
|
||||
SECTION:=net
|
||||
CATEGORY:=Network
|
||||
TITLE:=Telegram messenger module for internet-detector
|
||||
URL:=https://github.com/gSpotx2f/luci-app-internet-detector
|
||||
PKGARCH:=all
|
||||
DEPENDS:=+internet-detector +curl
|
||||
endef
|
||||
|
||||
define Package/$(PKG_NAME)/description
|
||||
Telegram messenger support for internet-detector.
|
||||
endef
|
||||
|
||||
define Package/$(PKG_NAME)/conffiles
|
||||
endef
|
||||
|
||||
define Build/Configure
|
||||
endef
|
||||
|
||||
define Build/Compile
|
||||
endef
|
||||
|
||||
define Package/$(PKG_NAME)/install
|
||||
$(INSTALL_DIR) $(1)/usr/lib/lua/internet-detector/modules
|
||||
$(INSTALL_DATA) ./files/usr/lib/lua/internet-detector/modules/mod_telegram.lua $(1)/usr/lib/lua/internet-detector/modules/mod_telegram.lua
|
||||
endef
|
||||
|
||||
$(eval $(call BuildPackage,$(PKG_NAME)))
|
||||
@@ -0,0 +1,264 @@
|
||||
--[[
|
||||
Dependences:
|
||||
curl
|
||||
--]]
|
||||
|
||||
local unistd = require("posix.unistd")
|
||||
|
||||
local Module = {
|
||||
name = "mod_telegram",
|
||||
runPrio = 70,
|
||||
syslog = function(level, msg) return true end,
|
||||
debugOutput = function(msg) return true end,
|
||||
writeValue = function(filePath, str) return false end,
|
||||
readValue = function(filePath) return nil end,
|
||||
deadPeriod = 0,
|
||||
alivePeriod = 0,
|
||||
mode = 0, -- 0: connected, 1: disconnected, 2: both
|
||||
hostAlias = "OpenWrt",
|
||||
connectTimeout = 5,
|
||||
tgAPIToken = nil,
|
||||
tgChatId = nil,
|
||||
tgMsgURLpattern = "https://api.telegram.org/bot%s/sendMessage?chat_id=%s&parse_mode=html&text=%s",
|
||||
msgTextPattern = "<strong>[%s] (%s)</strong> @ %s", -- Message (host, instance, message)
|
||||
msgConnectPattern = "Connected: %s",
|
||||
msgDisconnectPattern = "Disconnected: %s",
|
||||
msgSeparator = " | ",
|
||||
msgMaxItems = 50,
|
||||
msgSendAttempts = 3,
|
||||
msgSendTimeout = 5,
|
||||
curlExec = "/usr/bin/curl",
|
||||
curlParams = "-s",
|
||||
status = nil,
|
||||
_enabled = false,
|
||||
_deadCounter = 0,
|
||||
_aliveCounter = 0,
|
||||
_msgSentDisconnect = true,
|
||||
_disconnected = true,
|
||||
_msgSentConnect = true,
|
||||
_connected = true,
|
||||
_msgBuffer = {},
|
||||
_msgSendCounter = 3,
|
||||
_msgTimeoutCounter = 5,
|
||||
}
|
||||
|
||||
local function prequire(package)
|
||||
local retVal, pkg = pcall(require, package)
|
||||
return retVal and pkg
|
||||
end
|
||||
|
||||
function Module:init(t)
|
||||
self._enabled = true
|
||||
if t.mode ~= nil then
|
||||
self.mode = tonumber(t.mode)
|
||||
end
|
||||
if t.dead_period ~= nil then
|
||||
self.deadPeriod = tonumber(t.dead_period)
|
||||
end
|
||||
if t.alive_period ~= nil then
|
||||
self.alivePeriod = tonumber(t.alive_period)
|
||||
end
|
||||
if t.host_alias then
|
||||
self.hostAlias = t.host_alias
|
||||
else
|
||||
self.hostAlias = self.config.hostname
|
||||
end
|
||||
if t.api_token ~= nil then
|
||||
self.tgAPIToken = t.api_token
|
||||
end
|
||||
if t.chat_id ~= nil then
|
||||
self.tgChatId = t.chat_id
|
||||
end
|
||||
if tonumber(t.message_at_startup) == 1 then
|
||||
self._msgSentDisconnect = false
|
||||
self._disconnected = false
|
||||
self._msgSentConnect = false
|
||||
self._connected = false
|
||||
end
|
||||
|
||||
if unistd.access(self.curlExec, "x") then
|
||||
self._enabled = true
|
||||
else
|
||||
self._enabled = false
|
||||
self.syslog("err", string.format("%s: %s is not available", self.name, self.curlExec))
|
||||
end
|
||||
if not self.tgAPIToken then
|
||||
self._enabled = false
|
||||
self.syslog("err", string.format("%s: Telegram bot API token not specified.", self.name))
|
||||
end
|
||||
if not self.tgChatId then
|
||||
self._enabled = false
|
||||
self.syslog("err", string.format("%s: Telegram chat ID not specified.", self.name))
|
||||
end
|
||||
|
||||
self._msgSendCounter = self.msgSendAttempts
|
||||
end
|
||||
|
||||
function Module:escape(str)
|
||||
local t = {}
|
||||
for i in str:gmatch(".") do
|
||||
if i:match("[^%w_]") then
|
||||
t[#t + 1] = "%" .. string.format("%x", string.byte(i))
|
||||
else
|
||||
t[#t + 1] = i
|
||||
end
|
||||
end
|
||||
return table.concat(t)
|
||||
end
|
||||
|
||||
function Module:appendNotice(str)
|
||||
self._msgBuffer[#self._msgBuffer + 1] = str
|
||||
if #self._msgBuffer > self.msgMaxItems then
|
||||
local t = {}
|
||||
for i = #self._msgBuffer - self.msgMaxItems + 1, #self._msgBuffer do
|
||||
t[#t + 1] = self._msgBuffer[i]
|
||||
end
|
||||
self._msgBuffer = t
|
||||
end
|
||||
end
|
||||
|
||||
function Module:httpRequest(url)
|
||||
local retCode = 1, data
|
||||
local fh = io.popen(string.format(
|
||||
'%s --connect-timeout %s %s "%s"; printf "\n$?";', self.curlExec, self.connectTimeout, self.curlParams, url), "r")
|
||||
if fh then
|
||||
data = fh:read("*a")
|
||||
fh:close()
|
||||
local s, e = data:find("[0-9]+\n?$")
|
||||
retCode = tonumber(data:sub(s))
|
||||
data = data:sub(0, s - 2)
|
||||
if not data or data == "" then
|
||||
data = nil
|
||||
end
|
||||
else
|
||||
retCode = 1
|
||||
end
|
||||
return retCode, data
|
||||
end
|
||||
|
||||
function Module:parseResponse(str)
|
||||
local ok, errCode, desc
|
||||
ok = str:match('"ok":(%w+)')
|
||||
if ok == "false" then
|
||||
errCode = tonumber(str:match('"error_code":(%d+)'))
|
||||
if errCode then
|
||||
desc = str:match('"description":"([%w%s%p_]+)"')
|
||||
end
|
||||
end
|
||||
return ok, errCode, desc
|
||||
end
|
||||
|
||||
function Module:messageRequest(msg, textPattern)
|
||||
local retVal = 1
|
||||
local tgMsg = string.format(
|
||||
textPattern, self.hostAlias, self.config.serviceConfig.instance, msg)
|
||||
local url = string.format(
|
||||
self.tgMsgURLpattern, self.tgAPIToken, self.tgChatId, self:escape(tgMsg))
|
||||
|
||||
local ok, errCode, desc
|
||||
local retCode, data = self:httpRequest(url)
|
||||
if data then
|
||||
ok, errCode, desc = self:parseResponse(data)
|
||||
end
|
||||
if retCode == 0 and ok == "true" then
|
||||
retVal = 0
|
||||
self.syslog("info", string.format(
|
||||
"%s: Message sent to chat %s", self.name, self.tgChatId))
|
||||
else
|
||||
if errCode == 400 or errCode == 406 then
|
||||
retVal = 2
|
||||
elseif (errCode == 401 or
|
||||
errCode == 403 or
|
||||
errCode == 404 or
|
||||
errCode == 420) then
|
||||
retVal = 3
|
||||
end
|
||||
if errCode and desc then
|
||||
self.syslog("warning", string.format(
|
||||
"%s: %s %s", self.name, tostring(errCode), tostring(desc)))
|
||||
end
|
||||
end
|
||||
return retVal
|
||||
end
|
||||
|
||||
function Module:sendMessage(msg, textPattern)
|
||||
local retVal = self:messageRequest(msg, textPattern)
|
||||
if retVal == 0 then
|
||||
self._msgBuffer = {}
|
||||
elseif retVal == 2 then
|
||||
self.syslog("err", string.format(
|
||||
"%s: Server error (invalid API token or chat ID)", self.name))
|
||||
else
|
||||
self.syslog("err", string.format(
|
||||
"%s: An error occured while sending message", self.name))
|
||||
end
|
||||
return retVal
|
||||
end
|
||||
|
||||
function Module:run(currentStatus, lastStatus, timeDiff, timeNow, inetChecked)
|
||||
if not self._enabled then
|
||||
return
|
||||
end
|
||||
|
||||
if currentStatus == 1 then
|
||||
self._aliveCounter = 0
|
||||
self._msgSentConnect = false
|
||||
|
||||
if not self._disconnected then
|
||||
self._disconnected = true
|
||||
self:appendNotice(string.format(
|
||||
self.msgDisconnectPattern, os.date("%Y.%m.%d %H:%M:%S", os.time())))
|
||||
end
|
||||
if not self._msgSentDisconnect and (self.mode == 1 or self.mode == 2) then
|
||||
if self._deadCounter >= self.deadPeriod then
|
||||
self._msgSendCounter = 0
|
||||
self._msgSentDisconnect = true
|
||||
else
|
||||
self._deadCounter = self._deadCounter + timeDiff
|
||||
end
|
||||
end
|
||||
self._connected = false
|
||||
else
|
||||
self._deadCounter = 0
|
||||
self._msgSentDisconnect = false
|
||||
|
||||
if not self._connected then
|
||||
self._connected = true
|
||||
self:appendNotice(string.format(
|
||||
self.msgConnectPattern, os.date("%Y.%m.%d %H:%M:%S", os.time())))
|
||||
end
|
||||
if not self._msgSentConnect and (self.mode == 0 or self.mode == 2) then
|
||||
if self._aliveCounter >= self.alivePeriod then
|
||||
self._msgSendCounter = 0
|
||||
self._msgSentConnect = true
|
||||
else
|
||||
self._aliveCounter = self._aliveCounter + timeDiff
|
||||
end
|
||||
end
|
||||
self._disconnected = false
|
||||
end
|
||||
|
||||
if self._msgSendCounter < self.msgSendAttempts then
|
||||
if self._msgTimeoutCounter >= self.msgSendTimeout then
|
||||
if #self._msgBuffer > 0 then
|
||||
local retVal = self:sendMessage(table.concat(self._msgBuffer, self.msgSeparator), self.msgTextPattern)
|
||||
if retVal == 1 then
|
||||
self._msgSendCounter = self._msgSendCounter + 1
|
||||
else
|
||||
self._msgSendCounter = self.msgSendAttempts
|
||||
end
|
||||
end
|
||||
self._msgTimeoutCounter = 0
|
||||
else
|
||||
self._msgTimeoutCounter = self._msgTimeoutCounter + timeDiff
|
||||
end
|
||||
else
|
||||
self._msgTimeoutCounter = self.msgSendTimeout
|
||||
end
|
||||
end
|
||||
|
||||
function Module:onExit()
|
||||
return true
|
||||
end
|
||||
|
||||
return Module
|
||||
@@ -5,7 +5,7 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=internet-detector
|
||||
PKG_VERSION:=1.5.2
|
||||
PKG_VERSION:=1.6.0
|
||||
PKG_RELEASE:=1
|
||||
PKG_MAINTAINER:=gSpot <https://github.com/gSpotx2f/luci-app-internet-detector>
|
||||
|
||||
|
||||
@@ -44,6 +44,11 @@ config instance 'internet'
|
||||
option mod_email_mode '0'
|
||||
option mod_email_alive_period '0'
|
||||
option mod_email_mail_security 'tls'
|
||||
option mod_telegram_enabled '0'
|
||||
option mod_telegram_message_at_startup '0'
|
||||
option mod_telegram_mode '2'
|
||||
option mod_telegram_dead_period '0'
|
||||
option mod_telegram_alive_period '0'
|
||||
option mod_user_scripts_enabled '0'
|
||||
option mod_user_scripts_alive_period '0'
|
||||
option mod_user_scripts_up_script_attempts '1'
|
||||
|
||||
@@ -20,39 +20,69 @@ local Module = {
|
||||
port = 53,
|
||||
runInterval = 600,
|
||||
runIntervalFailed = 60,
|
||||
runIntervalDNSFailed = 1,
|
||||
runIntervalIPFailed = 1,
|
||||
requestAttempts = 2,
|
||||
timeout = 3,
|
||||
curlExec = "/usr/bin/curl",
|
||||
curlParams = "-s",
|
||||
providers = {
|
||||
opendns1 = {
|
||||
name = "opendns1", host = "myip.opendns.com",
|
||||
name = "opendns1", type = "dns", host = "myip.opendns.com",
|
||||
server = "208.67.222.222", server6 = "2620:119:35::35",
|
||||
port = 53, queryType = "A", queryType6 = "AAAA",
|
||||
},
|
||||
opendns2 = {
|
||||
name = "opendns2", host = "myip.opendns.com",
|
||||
opendns2 = {
|
||||
name = "opendns2", type = "dns", host = "myip.opendns.com",
|
||||
server = "208.67.220.220", server6 = "2620:119:35::35",
|
||||
port = 53, queryType = "A", queryType6 = "AAAA",
|
||||
},
|
||||
opendns3 = {
|
||||
name = "opendns3", host = "myip.opendns.com",
|
||||
opendns3 = {
|
||||
name = "opendns3", type = "dns", host = "myip.opendns.com",
|
||||
server = "208.67.222.220", server6 = "2620:119:35::35",
|
||||
port = 53, queryType = "A", queryType6 = "AAAA",
|
||||
},
|
||||
opendns4 = {
|
||||
name = "opendns4", host = "myip.opendns.com",
|
||||
opendns4 = {
|
||||
name = "opendns4", type = "dns", host = "myip.opendns.com",
|
||||
server = "208.67.220.222", server6 = "2620:119:35::35",
|
||||
port = 53, queryType = "A", queryType6 = "AAAA",
|
||||
},
|
||||
akamai = {
|
||||
name = "akamai", host = "whoami.akamai.net",
|
||||
google = {
|
||||
name = "google", type = "dns", host = "o-o.myaddr.l.google.com",
|
||||
server = "ns1.google.com", server6 = "ns1.google.com",
|
||||
port = 53, queryType = "TXT", queryType6 = "TXT",
|
||||
},
|
||||
akamai = {
|
||||
name = "akamai", type = "dns", host = "whoami.akamai.net",
|
||||
server = "ns1-1.akamaitech.net", server6 = "ns1-1.akamaitech.net",
|
||||
port = 53, queryType = "A", queryType6 = "AAAA",
|
||||
},
|
||||
google = {
|
||||
name = "google", host = "o-o.myaddr.l.google.com",
|
||||
server = "ns1.google.com", server6 = "ns1.google.com",
|
||||
port = 53, queryType = "TXT", queryType6 = "TXT",
|
||||
akamai_http = {
|
||||
name = "akamai_http", type = "http", url = "http://whatismyip.akamai.com/",
|
||||
parseResponseFunc = nil,
|
||||
},
|
||||
amazonaws= {
|
||||
name = "amazonaws", type = "http", url = "http://checkip.amazonaws.com/",
|
||||
parseResponseFunc = nil,
|
||||
},
|
||||
wgetip= {
|
||||
name = "wgetip", type = "http", url = "http://wgetip.com/",
|
||||
parseResponseFunc = nil,
|
||||
},
|
||||
ifconfig= {
|
||||
name = "ifconfig", type = "http", url = "http://ifconfig.me/",
|
||||
parseResponseFunc = nil,
|
||||
},
|
||||
ipecho= {
|
||||
name = "ipecho", type = "http", url = "http://ipecho.net/plain",
|
||||
parseResponseFunc = nil,
|
||||
},
|
||||
canhazip= {
|
||||
name = "canhazip", type = "http", url = "http://canhazip.com/",
|
||||
parseResponseFunc = nil,
|
||||
},
|
||||
icanhazip = {
|
||||
name = "icanhazip", type = "http", url = "http://icanhazip.com/",
|
||||
parseResponseFunc = nil,
|
||||
},
|
||||
},
|
||||
ipScript = "",
|
||||
@@ -64,9 +94,10 @@ local Module = {
|
||||
_lastResolvedIp = nil,
|
||||
_enabled = false,
|
||||
_counter = 0,
|
||||
_DNSFalseCounter = 0,
|
||||
_IPFalseCounter = 0,
|
||||
_interval = 600,
|
||||
_DNSPacket = nil,
|
||||
_requestIP = nil,
|
||||
}
|
||||
|
||||
function Module:runIpScript()
|
||||
@@ -172,6 +203,7 @@ function Module:sendUDPMessage(message, server, port)
|
||||
end
|
||||
|
||||
local ok, errMsg, errNum = socket.sendto(sock, message, saTable[1])
|
||||
|
||||
local response = {}
|
||||
if ok then
|
||||
local ret, resp, errNum = socket.recvfrom(sock, 1024)
|
||||
@@ -212,13 +244,10 @@ end
|
||||
function Module:parseParts(message, start, parts)
|
||||
local partStart = start + 2
|
||||
local partLen = message:sub(start, start + 1)
|
||||
|
||||
if #partLen == 0 then
|
||||
return parts
|
||||
end
|
||||
|
||||
local partEnd = partStart + (tonumber(partLen, 16) * 2)
|
||||
|
||||
parts[#parts + 1] = message:sub(partStart, partEnd - 1)
|
||||
if message:sub(partEnd, partEnd + 1) == "00" or partEnd > #message then
|
||||
return parts
|
||||
@@ -240,6 +269,7 @@ function Module:decodeMessage(message)
|
||||
local ARCOUNT = message:sub(21, 24)
|
||||
|
||||
local questionSectionStarts = 25
|
||||
|
||||
local questionParts = self:parseParts(message, questionSectionStarts, {})
|
||||
local qtypeStarts = questionSectionStarts + (#table.concat(questionParts)) + (#questionParts * 2) + 1
|
||||
local qclassStarts = qtypeStarts + 4
|
||||
@@ -251,11 +281,11 @@ function Module:decodeMessage(message)
|
||||
if numAnswers > 0 then
|
||||
for answerCount = 1, numAnswers do
|
||||
if answerSectionStarts < #message then
|
||||
local ATYPE = tonumber(
|
||||
local ATYPE = tonumber(
|
||||
message:sub(answerSectionStarts + 5, answerSectionStarts + 8), 16)
|
||||
local RDLENGTH = tonumber(
|
||||
local RDLENGTH = tonumber(
|
||||
message:sub(answerSectionStarts + 21, answerSectionStarts + 24), 16)
|
||||
local RDDATA = message:sub(
|
||||
local RDDATA = message:sub(
|
||||
answerSectionStarts + 25, answerSectionStarts + 24 + (RDLENGTH * 2))
|
||||
local RDDATA_decoded = ""
|
||||
|
||||
@@ -290,26 +320,23 @@ function Module:decodeMessage(message)
|
||||
end
|
||||
answerSectionStarts = answerSectionStarts + 24 + (RDLENGTH * 2)
|
||||
|
||||
if RDDATA_decoded:match("^[a-f0-9.:]+$") then
|
||||
if RDDATA_decoded:match("^[a-fA-F0-9.:]+$") then
|
||||
retTable[#retTable + 1] = RDDATA_decoded
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return retTable
|
||||
end
|
||||
|
||||
function Module:requestIP()
|
||||
function Module:requestIPDNS()
|
||||
local res
|
||||
local qtype = self._qtype and self._provider.queryType6 or self._provider.queryType
|
||||
local server = self._qtype and self._provider.server6 or self._provider.server
|
||||
local port = self._provider.port or self.port
|
||||
|
||||
if not self._DNSPacket then
|
||||
self._DNSPacket = self:buildMessage(self._provider.host, qtype)
|
||||
end
|
||||
|
||||
local retCode, response = self:sendUDPMessage(self._DNSPacket, server, port)
|
||||
if retCode == 0 and response then
|
||||
local retTable = self:decodeMessage(response)
|
||||
@@ -320,7 +347,57 @@ function Module:requestIP()
|
||||
self.syslog("warning", string.format(
|
||||
"%s: UDP error when requesting an IP address", self.name))
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
function Module:httpRequest(url)
|
||||
local retCode = 1, data
|
||||
local iface = ""
|
||||
if self.config.serviceConfig.iface then
|
||||
iface = " --interface " .. self.config.serviceConfig.iface
|
||||
end
|
||||
local fh = io.popen(string.format(
|
||||
'%s%s --connect-timeout %s %s "%s"; printf "\n$?";', self.curlExec, iface, self.timeout, self.curlParams, url), "r")
|
||||
if fh then
|
||||
data = fh:read("*a")
|
||||
fh:close()
|
||||
local s, e = data:find("[0-9]+\n?$")
|
||||
retCode = tonumber(data:sub(s))
|
||||
data = data:sub(0, s - 2)
|
||||
if not data or data == "" then
|
||||
data = nil
|
||||
end
|
||||
else
|
||||
retCode = 1
|
||||
end
|
||||
return retCode, data
|
||||
end
|
||||
|
||||
function Module:parseHTTPResponse(data)
|
||||
data = data:gsub("^[%s%c]+", ""):gsub("[%s%c]+$", "")
|
||||
if data:match("^[a-fA-F0-9.:]+$") then
|
||||
return data
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
function Module:requestIPHTTP()
|
||||
local res
|
||||
local url = self._provider.url
|
||||
local parseResponseFunc = self._provider.parseResponseFunc
|
||||
if url then
|
||||
local retCode, data = self:httpRequest(url)
|
||||
if retCode == 0 and data then
|
||||
if type(parseResponseFunc) == "function" then
|
||||
res = parseResponseFunc(data)
|
||||
else
|
||||
res = self:parseHTTPResponse(data)
|
||||
end
|
||||
else
|
||||
self.syslog("warning", string.format(
|
||||
"%s: HTTP error when requesting an IP address", self.name))
|
||||
end
|
||||
end
|
||||
return res
|
||||
end
|
||||
|
||||
@@ -352,12 +429,28 @@ function Module:init(t)
|
||||
if t.qtype ~= nil then
|
||||
self._qtype = (tonumber(t.qtype) ~= 0)
|
||||
end
|
||||
self._currentIp = nil
|
||||
self._lastResolvedIp = nil
|
||||
self._DNSPacket = nil
|
||||
self._interval = self.runInterval
|
||||
self._DNSFalseCounter = 0
|
||||
self._enabled = true
|
||||
self._currentIp = nil
|
||||
self._lastResolvedIp = nil
|
||||
self._DNSPacket = nil
|
||||
self._interval = self.runInterval
|
||||
self._IPFalseCounter = 0
|
||||
self._enabled = true
|
||||
if not self._provider then
|
||||
self._enabled = false
|
||||
else
|
||||
if self._provider.url and not unistd.access(self.curlExec, "x") then
|
||||
self._enabled = false
|
||||
self.syslog("err", string.format(
|
||||
"%s: %s is not available. You need to install curl.", self.name, self.curlExec))
|
||||
end
|
||||
if self._provider.type == "dns" then
|
||||
self._requestIP = self.requestIPDNS
|
||||
elseif self._provider.type == "http" then
|
||||
self._requestIP = self.requestIPHTTP
|
||||
else
|
||||
self._enabled = false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function Module:run(currentStatus, lastStatus, timeDiff, timeNow, inetChecked)
|
||||
@@ -366,23 +459,20 @@ function Module:run(currentStatus, lastStatus, timeDiff, timeNow, inetChecked)
|
||||
end
|
||||
if currentStatus == 0 then
|
||||
if self._counter == 0 or self._counter >= self._interval or currentStatus ~= lastStatus then
|
||||
|
||||
local ip = self:requestIP()
|
||||
|
||||
local ip = self:_requestIP()
|
||||
if not ip then
|
||||
ip = ""
|
||||
self._DNSFalseCounter = self._DNSFalseCounter + 1
|
||||
if self._DNSFalseCounter >= self.requestAttempts then
|
||||
ip = ""
|
||||
self._IPFalseCounter = self._IPFalseCounter + 1
|
||||
if self._IPFalseCounter >= self.requestAttempts then
|
||||
self._interval = self.runIntervalFailed
|
||||
self._DNSFalseCounter = 0
|
||||
self._IPFalseCounter = 0
|
||||
else
|
||||
self._interval = self.runIntervalDNSFailed
|
||||
self._interval = self.runIntervalIPFailed
|
||||
end
|
||||
else
|
||||
self._interval = self.runInterval
|
||||
self._DNSFalseCounter = 0
|
||||
self._interval = self.runInterval
|
||||
self._IPFalseCounter = 0
|
||||
end
|
||||
|
||||
if ip ~= self._currentIp then
|
||||
self.status = ip
|
||||
if ip ~= "" then
|
||||
@@ -400,11 +490,11 @@ function Module:run(currentStatus, lastStatus, timeDiff, timeNow, inetChecked)
|
||||
self._counter = 0
|
||||
end
|
||||
else
|
||||
self._currentIp = nil
|
||||
self.status = self._currentIp
|
||||
self._DNSFalseCounter = 0
|
||||
self._counter = 0
|
||||
self._interval = self.runInterval
|
||||
self._currentIp = nil
|
||||
self.status = self._currentIp
|
||||
self._IPFalseCounter = 0
|
||||
self._counter = 0
|
||||
self._interval = self.runInterval
|
||||
end
|
||||
self._counter = self._counter + timeDiff
|
||||
end
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
include $(TOPDIR)/rules.mk
|
||||
|
||||
PKG_NAME:=luci-app-internet-detector
|
||||
PKG_VERSION:=1.5.2
|
||||
PKG_VERSION:=1.6.0
|
||||
PKG_RELEASE:=1
|
||||
LUCI_TITLE:=LuCI support for internet-detector
|
||||
LUCI_DEPENDS:=+internet-detector
|
||||
|
||||
@@ -134,6 +134,33 @@ var Timefield = ui.Textfield.extend({
|
||||
},
|
||||
});
|
||||
|
||||
var TextfieldButton = ui.Textfield.extend({
|
||||
render() {
|
||||
let frameEl = E('div', { 'id': this.options.id }),
|
||||
inputEl = E('input', {
|
||||
'id' : this.options.id ? 'widget.' + this.options.id : null,
|
||||
'name' : this.options.name,
|
||||
'type' : 'text',
|
||||
'class' : 'cbi-input-text',
|
||||
'readonly' : this.options.readonly ? '' : null,
|
||||
'disabled' : this.options.disabled ? '' : null,
|
||||
'maxlength' : this.options.maxlength,
|
||||
'placeholder': this.options.placeholder,
|
||||
'value' : this.value,
|
||||
});
|
||||
frameEl.appendChild(E('div', { 'class': 'control-group' }, [
|
||||
inputEl,
|
||||
E('button', {
|
||||
'class' : `cbi-button cbi-button-${this.options.btnstyle || 'neutral'}`,
|
||||
'title' : this.options.btntitle,
|
||||
'aria-label': this.options.btntitle,
|
||||
'click' : this.options.onclick,
|
||||
}, this.options.btntext,)
|
||||
]));
|
||||
return this.bind(frameEl);
|
||||
},
|
||||
});
|
||||
|
||||
return view.extend({
|
||||
appName : 'internet-detector',
|
||||
configDir : '/etc/internet-detector',
|
||||
@@ -149,10 +176,13 @@ return view.extend({
|
||||
ledsPath : '/sys/class/leds',
|
||||
ledsPerInstance : 3,
|
||||
leds : [],
|
||||
tgUpdatesURLPattern : 'https://api.telegram.org/bot%s/getUpdates',
|
||||
mm : false,
|
||||
mmInit : false,
|
||||
email : false,
|
||||
emailExec : false,
|
||||
telegram : false,
|
||||
curlExec : false,
|
||||
modRegularScriptNextRun: {},
|
||||
|
||||
callInitStatus: rpc.declare({
|
||||
@@ -330,6 +360,61 @@ return view.extend({
|
||||
});
|
||||
},
|
||||
|
||||
getTgChatIdHandler(ev, instance) {
|
||||
ev.preventDefault();
|
||||
let botToken;
|
||||
let botTokenInput = document.getElementById(
|
||||
'widget.cbid.%s.%s.mod_telegram_api_token'.format(this.appName, instance));
|
||||
if(botTokenInput) {
|
||||
botToken = botTokenInput.value;
|
||||
};
|
||||
if(!botTokenInput || !botToken) {
|
||||
alert(_('Bot API token is missing!'));
|
||||
return;
|
||||
};
|
||||
let apiURL = this.tgUpdatesURLPattern.format(botToken);
|
||||
|
||||
console.log(`Requesting chat ID: ${apiURL}`);
|
||||
|
||||
return fetch(apiURL).then(r => {
|
||||
if(r.ok) {
|
||||
r.json().then(j => {
|
||||
let chats = [];
|
||||
if(j.ok && j.result) {
|
||||
j.result.forEach(i => {
|
||||
if(i.message && i.message.chat && i.message.chat.id) {
|
||||
if(!chats.includes(i.message.chat.id)) {
|
||||
chats.push(i.message.chat.id);
|
||||
};
|
||||
};
|
||||
});
|
||||
};
|
||||
let tgChatIdInput = document.getElementById(
|
||||
'widget.cbid.%s.%s.mod_telegram_chat_id'.format(this.appName, instance));
|
||||
if(tgChatIdInput) {
|
||||
if(chats.length == 0) {
|
||||
alert(_('No messages available. Write something to the bot and try again.'));
|
||||
} else {
|
||||
tgChatIdInput.value = chats[chats.length - 1];
|
||||
tgChatIdInput.focus();
|
||||
tgChatIdInput.blur();
|
||||
};
|
||||
};
|
||||
});
|
||||
} else {
|
||||
let status = r.status;
|
||||
let errorString = `${_('Error')} ${r.status}.`;
|
||||
if(status == 404) {
|
||||
errorString += ` ${_('Incorrect bot token?')}`;
|
||||
};
|
||||
alert(errorString);
|
||||
};
|
||||
}).catch(e => {
|
||||
alert(e.message);
|
||||
throw e;
|
||||
});
|
||||
},
|
||||
|
||||
CBITimeInput: form.Value.extend({
|
||||
__name__ : 'CBI.TimeInput',
|
||||
|
||||
@@ -354,6 +439,27 @@ return view.extend({
|
||||
},
|
||||
}),
|
||||
|
||||
CBITextfieldButtonInput: form.Value.extend({
|
||||
__name__ : 'CBI.TextfieldButtonInput',
|
||||
|
||||
renderWidget(section_id, option_index, cfgvalue) {
|
||||
let value = (cfgvalue != null) ? cfgvalue : this.default,
|
||||
widget = new TextfieldButton(value, {
|
||||
id : this.cbid(section_id),
|
||||
optional : this.optional || this.rmempty,
|
||||
datatype : this.datatype,
|
||||
placeholder: this.placeholder,
|
||||
validate : L.bind(this.validate, this, section_id),
|
||||
disabled : (this.readonly != null) ? this.readonly : this.map.readonly,
|
||||
btntext : this.btntext,
|
||||
btntitle : this.btntitle,
|
||||
btnstyle : this.btnstyle,
|
||||
onclick : this.onclick,
|
||||
});
|
||||
return widget.render();
|
||||
},
|
||||
}),
|
||||
|
||||
CBIBlockInetStatus: form.Value.extend({
|
||||
__name__ : 'CBI.BlockInetStatus',
|
||||
|
||||
@@ -542,6 +648,12 @@ return view.extend({
|
||||
if(data[3].email_exec) {
|
||||
this.emailExec = true;
|
||||
};
|
||||
if(data[3].telegram) {
|
||||
this.telegram = true;
|
||||
};
|
||||
if(data[3].curl_exec) {
|
||||
this.curlExec = true;
|
||||
};
|
||||
};
|
||||
this.currentAppMode = uci.get(this.appName, 'config', 'mode');
|
||||
|
||||
@@ -756,6 +868,9 @@ return view.extend({
|
||||
if(this.email) {
|
||||
s.tab('email', _('Email notification'));
|
||||
};
|
||||
if(this.telegram) {
|
||||
s.tab('telegram', _('Telegram notification'));
|
||||
};
|
||||
s.tab('user_scripts', _('User scripts'));
|
||||
s.tab('regular_script', _('Regular script'));
|
||||
};
|
||||
@@ -1081,16 +1196,27 @@ return view.extend({
|
||||
|
||||
// provider
|
||||
o = s.taboption('public_ip', form.ListValue,
|
||||
'mod_public_ip_provider', _('DNS provider'),
|
||||
_('Service for determining the public IP address through DNS.')
|
||||
'mod_public_ip_provider', _('Provider'),
|
||||
_('Service for determining the public IP address.') + '<br />' +
|
||||
((this.curlExec) ? '' :
|
||||
_('To support HTTP services you need to install curl.'))
|
||||
);
|
||||
o.modalonly = true;
|
||||
o.value('opendns1');
|
||||
o.value('opendns2');
|
||||
o.value('opendns3');
|
||||
o.value('opendns4');
|
||||
o.value('akamai');
|
||||
o.value('google');
|
||||
o.value('opendns1', 'opendns1 (DNS)');
|
||||
o.value('opendns2', 'opendns2 (DNS)');
|
||||
o.value('opendns3', 'opendns3 (DNS)');
|
||||
o.value('opendns4', 'opendns4 (DNS)');
|
||||
o.value('google', 'google (DNS)');
|
||||
o.value('akamai', 'akamai (DNS)');
|
||||
if(this.curlExec) {
|
||||
o.value('akamai_http', "akamai (HTTP)");
|
||||
o.value('amazonaws', "amazonaws (HTTP)");
|
||||
o.value('wgetip', "wgetip.com (HTTP)");
|
||||
o.value('ifconfig', "ifconfig.me (HTTP)");
|
||||
o.value('ipecho', "ipecho.net (HTTP)");
|
||||
o.value('canhazip', "canhazip.com (HTTP)");
|
||||
o.value('icanhazip', "icanhazip.com (HTTP)");
|
||||
};
|
||||
o.default = 'opendns1';
|
||||
|
||||
// ipv6
|
||||
@@ -1102,6 +1228,12 @@ return view.extend({
|
||||
o.value('0', 'A (IPv4)');
|
||||
o.value('1', 'AAAA (IPv6)');
|
||||
o.default = '0';
|
||||
o.depends({ 'mod_public_ip_provider': 'opendns1' });
|
||||
o.depends({ 'mod_public_ip_provider': 'opendns2' });
|
||||
o.depends({ 'mod_public_ip_provider': 'opendns3' });
|
||||
o.depends({ 'mod_public_ip_provider': 'opendns4' });
|
||||
o.depends({ 'mod_public_ip_provider': 'google' });
|
||||
o.depends({ 'mod_public_ip_provider': 'akamai' });
|
||||
|
||||
// interval
|
||||
o = s.taboption('public_ip', form.ListValue,
|
||||
@@ -1290,6 +1422,103 @@ return view.extend({
|
||||
};
|
||||
};
|
||||
|
||||
// Telegram notification
|
||||
|
||||
if(this.telegram) {
|
||||
if(this.curlExec) {
|
||||
o = s.taboption('telegram', form.DummyValue, '_dummy');
|
||||
o.rawhtml = true;
|
||||
o.default = '<div class="cbi-section-descr">' +
|
||||
_('Telegram message will be sent when connected or disconnected from the Internet.') +
|
||||
'<br />' +
|
||||
_("You need to register a new %sTelegram bot%s. Then get the bot's API token and paste it into the <code>Bot token</code> field. After that, open a chat with the bot, write something (in the Telegram app) and you will be able to get the chat ID using the <code>ID</code> button.").format("<a href='https://core.telegram.org/bots#how-do-i-create-a-bot' target='_blank'>", '</a>') +
|
||||
'</div>';
|
||||
|
||||
o.modalonly = true;
|
||||
|
||||
// enabled
|
||||
o = s.taboption('telegram', form.Flag, 'mod_telegram_enabled',
|
||||
_('Enable'));
|
||||
o.rmempty = false;
|
||||
o.modalonly = true;
|
||||
|
||||
// mode
|
||||
o = s.taboption('telegram', form.ListValue,
|
||||
'mod_telegram_mode', _('When message will be sent')
|
||||
);
|
||||
o.modalonly = true;
|
||||
o.value(0, _('after connection'));
|
||||
o.value(1, _('after disconnection'));
|
||||
o.value(2, _('after connection or disconnection'));
|
||||
o.default = '0';
|
||||
|
||||
// alive_period
|
||||
o = s.taboption('telegram', this.CBITimeInput,
|
||||
'mod_telegram_alive_period', _('Alive period'),
|
||||
_('Period of time after connecting to the Internet before sending a message.')
|
||||
);
|
||||
o.rmempty = false;
|
||||
o.modalonly = true;
|
||||
o.depends({ 'mod_telegram_mode': '0' });
|
||||
o.depends({ 'mod_telegram_mode': '2' });
|
||||
o.default = '0';
|
||||
|
||||
// dead_period
|
||||
o = s.taboption('telegram', this.CBITimeInput,
|
||||
'mod_telegram_dead_period', _('Dead period'),
|
||||
_('Period of time after disconnecting from Internet before sending a message.')
|
||||
);
|
||||
o.rmempty = false;
|
||||
o.modalonly = true;
|
||||
o.depends({ 'mod_telegram_mode': '1' });
|
||||
o.depends({ 'mod_telegram_mode': '2' });
|
||||
o.default = '0';
|
||||
|
||||
// host_alias
|
||||
o = s.taboption('telegram', form.Value, 'mod_telegram_host_alias',
|
||||
_('Host alias'),
|
||||
_('Host identifier in messages. If not specified, hostname will be used.'));
|
||||
o.modalonly = true;
|
||||
|
||||
// tg_api_token
|
||||
o = s.taboption('telegram', form.Value,
|
||||
'mod_telegram_api_token', _('Bot token'),
|
||||
_('Telegram bot API token.'));
|
||||
o.password = true;
|
||||
o.modalonly = true;
|
||||
|
||||
// tg_chat_id
|
||||
o = s.taboption('telegram', this.CBITextfieldButtonInput,
|
||||
'mod_telegram_chat_id', _('Chat ID'),
|
||||
_('ID of the Telegram chat to which messages will be sent.')
|
||||
);
|
||||
o.btntext = _('ID'),
|
||||
o.btntitle = _('Request chat ID from bot API'),
|
||||
o.btnstyle = 'action',
|
||||
o.onclick = ui.createHandlerFn(this,
|
||||
(ev) => this.getTgChatIdHandler(ev, s.section));
|
||||
o.modalonly = true;
|
||||
o.optional = false;
|
||||
o.rmempty = false;
|
||||
o.depends({ 'mod_telegram_api_token': /.+/ });
|
||||
|
||||
// message_at_startup
|
||||
o = s.taboption('telegram', form.Flag, 'mod_telegram_message_at_startup',
|
||||
_('On startup'),
|
||||
_('Send message on service startup.')
|
||||
);
|
||||
o.rmempty = false;
|
||||
o.modalonly = true;
|
||||
} else {
|
||||
o = s.taboption('telegram', form.DummyValue, '_dummy');
|
||||
o.rawhtml = true;
|
||||
o.default = '<label class="cbi-value-title"></label><div class="cbi-value-field"><em>' +
|
||||
_('Curl is not available...') +
|
||||
'</em></div>';
|
||||
o.modalonly = true;
|
||||
};
|
||||
};
|
||||
|
||||
// User scripts
|
||||
|
||||
o = s.taboption('user_scripts', form.DummyValue, '_dummy');
|
||||
|
||||
@@ -39,7 +39,7 @@ msgid "Alive period"
|
||||
msgstr "Период после подключения"
|
||||
|
||||
msgid "An email will be sent when connected or disconnected from the Internet."
|
||||
msgstr "Сообщение будет отправлено при подключении или отключении от Интернет."
|
||||
msgstr "Сообщение будет отправлено на email при подключении или отключении от Интернет."
|
||||
|
||||
msgid "An error has occurred"
|
||||
msgstr "Произошла ошибка"
|
||||
@@ -56,6 +56,15 @@ msgstr "Большой: 248 байт"
|
||||
msgid "Blink"
|
||||
msgstr "Мигание"
|
||||
|
||||
msgid "Bot API token is missing!"
|
||||
msgstr "Отсутствует API токен бота!"
|
||||
|
||||
msgid "Bot token"
|
||||
msgstr "Токен бота"
|
||||
|
||||
msgid "ID чата"
|
||||
msgstr ""
|
||||
|
||||
msgid "Check type"
|
||||
msgstr "Тип проверки"
|
||||
|
||||
@@ -80,6 +89,9 @@ msgstr "Таймаут соединения"
|
||||
msgid "Contents have been saved."
|
||||
msgstr "Содержимое сохранено."
|
||||
|
||||
msgid "Curl is not available..."
|
||||
msgstr "Curl недоступен..."
|
||||
|
||||
msgid "Dead interval"
|
||||
msgstr "Интервал при отключении"
|
||||
|
||||
@@ -113,9 +125,6 @@ msgstr "Закрыть"
|
||||
msgid "DNS query type"
|
||||
msgstr "Тип DNS-запроса"
|
||||
|
||||
msgid "DNS provider"
|
||||
msgstr "DNS провайдер"
|
||||
|
||||
msgid "Edit"
|
||||
msgstr "Изменить"
|
||||
|
||||
@@ -152,6 +161,9 @@ msgstr "Включить public-ip-script"
|
||||
msgid "Enabled"
|
||||
msgstr "Включен"
|
||||
|
||||
msgid "Error"
|
||||
msgstr "Ошибка"
|
||||
|
||||
msgid "Expecting:"
|
||||
msgstr "Ожидается:"
|
||||
|
||||
@@ -180,7 +192,7 @@ msgid "Hosts"
|
||||
msgstr "Хосты"
|
||||
|
||||
msgid "Hosts polling interval when the Internet is down."
|
||||
msgstr "Интервал опроса хостов если Интернет не доступен."
|
||||
msgstr "Интервал опроса хостов если Интернет недоступен."
|
||||
|
||||
msgid "Hosts polling interval when the Internet is up."
|
||||
msgstr "Интервал опроса хостов если Интернет доступен."
|
||||
@@ -195,12 +207,18 @@ msgstr ""
|
||||
msgid "Huge: 1492 bytes"
|
||||
msgstr "Огромный: 1492 байта"
|
||||
|
||||
msgid "ID of the Telegram chat to which messages will be sent."
|
||||
msgstr "ID чата Telegram в который будут отправлены сообщения."
|
||||
|
||||
msgid "ICMP-echo request (ping)"
|
||||
msgstr "Запрос ICMP-echo (ping)"
|
||||
|
||||
msgid "ICMP packet data size"
|
||||
msgstr "Размер данных ICMP-пакета"
|
||||
|
||||
msgid "Incorrect bot token?"
|
||||
msgstr "Неправильный токен бота?"
|
||||
|
||||
msgid "Instances"
|
||||
msgstr "Экземпляры"
|
||||
|
||||
@@ -250,7 +268,7 @@ msgid "Loading"
|
||||
msgstr "Загрузка"
|
||||
|
||||
msgid "Mailsend is not available..."
|
||||
msgstr "Mailsend не доступен..."
|
||||
msgstr "Mailsend недоступен..."
|
||||
|
||||
msgid "Main settings"
|
||||
msgstr "Основные настройки"
|
||||
@@ -280,7 +298,7 @@ msgid "Maximum timeout for waiting for a response from the host."
|
||||
msgstr "Максимальный таймаут ожидания ответа от хоста."
|
||||
|
||||
msgid "ModemManager is not available..."
|
||||
msgstr "ModemManager не доступен..."
|
||||
msgstr "ModemManager недоступен..."
|
||||
|
||||
msgid "Modem will be restarted when the Internet is disconnected."
|
||||
msgstr "Модем будет перезапущен при отключении Интернет."
|
||||
@@ -314,6 +332,9 @@ msgstr "Следующий запуск"
|
||||
msgid "No <abbr title=\"Light Emitting Diode\">LED</abbr>s available..."
|
||||
msgstr "Нет доступных <abbr title=\"Светодиод\">LED</abbr>..."
|
||||
|
||||
msgid "No messages available. Write something to the bot and try again."
|
||||
msgstr "Нет доступных сообщений. Напишите что-нибудь боту и попробуйте ещё раз."
|
||||
|
||||
msgid "Not scheduled"
|
||||
msgstr "Не запланирован"
|
||||
|
||||
@@ -373,6 +394,9 @@ msgstr "Регулярный скрипт"
|
||||
msgid "Polling interval"
|
||||
msgstr "Интервал опроса"
|
||||
|
||||
msgid "Provider"
|
||||
msgstr "Провайдер"
|
||||
|
||||
msgid "Public IP"
|
||||
msgstr "Публичный IP"
|
||||
|
||||
@@ -388,6 +412,9 @@ msgstr "Перезагрузка устройства если Интренет
|
||||
msgid "Recipient"
|
||||
msgstr "Получатель"
|
||||
|
||||
msgid "Request chat ID from bot API"
|
||||
msgstr "Запросить ID чата через API бота"
|
||||
|
||||
msgid "Restart"
|
||||
msgstr "Перезапуск"
|
||||
|
||||
@@ -463,8 +490,8 @@ msgstr "Не удалось выполнить действие службы \"%
|
||||
msgid "Service configuration"
|
||||
msgstr "Конфигурация службы"
|
||||
|
||||
msgid "Service for determining the public IP address through DNS."
|
||||
msgstr "Сервис для определения публичного IP адреса через DNS."
|
||||
msgid "Service for determining the public IP address."
|
||||
msgstr "Сервис для определения публичного IP адреса."
|
||||
|
||||
msgid "Service: detector always runs as a system service."
|
||||
msgstr "Служба: детектор работает постоянно, как системная служба."
|
||||
@@ -505,6 +532,12 @@ msgstr "TCP-порт"
|
||||
msgid "TCP port connection"
|
||||
msgstr "Подключение к TCP-порту"
|
||||
|
||||
msgid "Telegram bot API token."
|
||||
msgstr "API токен Telegram бота."
|
||||
|
||||
msgid "Telegram message will be sent when connected or disconnected from the Internet."
|
||||
msgstr "Сообщение в Telegram будет отправлено при подключении или отключении от Интернет."
|
||||
|
||||
msgid "The type of record requested in the DNS query (if the service supports it)."
|
||||
msgstr "Тип записи запрашиваемой в DNS-запросе (если сервис поддерживает)."
|
||||
|
||||
@@ -517,6 +550,9 @@ msgstr "Таймаут между остановкой и запуском се
|
||||
msgid "Timeout between stopping and starting a ModemManger interface when restarting."
|
||||
msgstr "Таймаут между остановкой и запуском интерфейса ModemManger при перезапуске."
|
||||
|
||||
msgid "To support HTTP services you need to install curl."
|
||||
msgstr "Для поддержки HTTP сервисов необходимо установить curl."
|
||||
|
||||
msgid "Type a time string"
|
||||
msgstr "Введите строку времени"
|
||||
|
||||
@@ -557,12 +593,20 @@ msgstr ""
|
||||
msgid "When email will be sent"
|
||||
msgstr "Когда будет отправлено сообщение"
|
||||
|
||||
msgid "When message will be sent"
|
||||
msgstr "Когда будет отправлено сообщение"
|
||||
|
||||
msgid "Windows: 32 bytes"
|
||||
msgstr "Windows: 32 байта"
|
||||
|
||||
msgid "Write messages to the system log."
|
||||
msgstr "Записывать сообщения в системный журнал."
|
||||
|
||||
msgid ""
|
||||
"You need to register a new %sTelegram bot%s. Then get the bot's API token and paste it into the <code>Bot token</code> field. After that, open a chat with the bot, write something (in the Telegram app) and you will be able to get the chat ID using the <code>ID</code> button."
|
||||
msgstr ""
|
||||
"Необходимо зарегистрировать новый %sTelegram бот%s. Затем получить API токен бота и вставить его в поле <code>Токен бота</code>. После этого, откройте чат с ботом, напишите что-нибудь (в приложении Telegram) и можно будет получить ID чата с помощью кнопки <code>ID</code>."
|
||||
|
||||
msgid "after connection"
|
||||
msgstr "после подключения"
|
||||
|
||||
|
||||
@@ -44,6 +44,15 @@ msgstr ""
|
||||
msgid "Blink"
|
||||
msgstr ""
|
||||
|
||||
msgid "Bot API token is missing!"
|
||||
msgstr ""
|
||||
|
||||
msgid "Bot token"
|
||||
msgstr ""
|
||||
|
||||
msgid "Chat ID"
|
||||
msgstr ""
|
||||
|
||||
msgid "Check type"
|
||||
msgstr ""
|
||||
|
||||
@@ -68,6 +77,9 @@ msgstr ""
|
||||
msgid "Contents have been saved."
|
||||
msgstr ""
|
||||
|
||||
msgid "Curl is not available..."
|
||||
msgstr ""
|
||||
|
||||
msgid "Dead interval"
|
||||
msgstr ""
|
||||
|
||||
@@ -101,9 +113,6 @@ msgstr ""
|
||||
msgid "DNS query type"
|
||||
msgstr ""
|
||||
|
||||
msgid "DNS provider"
|
||||
msgstr ""
|
||||
|
||||
msgid "Edit"
|
||||
msgstr ""
|
||||
|
||||
@@ -140,6 +149,9 @@ msgstr ""
|
||||
msgid "Enabled"
|
||||
msgstr ""
|
||||
|
||||
msgid "Error"
|
||||
msgstr ""
|
||||
|
||||
msgid "Expecting:"
|
||||
msgstr ""
|
||||
|
||||
@@ -181,12 +193,18 @@ msgstr ""
|
||||
msgid "Huge: 1492 bytes"
|
||||
msgstr ""
|
||||
|
||||
msgid "ID of the Telegram chat to which messages will be sent."
|
||||
msgstr ""
|
||||
|
||||
msgid "ICMP-echo request (ping)"
|
||||
msgstr ""
|
||||
|
||||
msgid "ICMP packet data size"
|
||||
msgstr ""
|
||||
|
||||
msgid "Incorrect bot token?"
|
||||
msgstr ""
|
||||
|
||||
msgid "Instances"
|
||||
msgstr ""
|
||||
|
||||
@@ -289,6 +307,9 @@ msgstr ""
|
||||
msgid "No <abbr title=\"Light Emitting Diode\">LED</abbr>s available..."
|
||||
msgstr ""
|
||||
|
||||
msgid "No messages available. Write something to the bot and try again."
|
||||
msgstr ""
|
||||
|
||||
msgid "Not scheduled"
|
||||
msgstr ""
|
||||
|
||||
@@ -345,6 +366,9 @@ msgstr ""
|
||||
msgid "Polling interval"
|
||||
msgstr ""
|
||||
|
||||
msgid "Provider"
|
||||
msgstr ""
|
||||
|
||||
msgid "Public IP"
|
||||
msgstr ""
|
||||
|
||||
@@ -360,6 +384,9 @@ msgstr ""
|
||||
msgid "Recipient"
|
||||
msgstr ""
|
||||
|
||||
msgid "Request chat ID from bot API"
|
||||
msgstr ""
|
||||
|
||||
msgid "Restart"
|
||||
msgstr ""
|
||||
|
||||
@@ -435,7 +462,7 @@ msgstr ""
|
||||
msgid "Service configuration"
|
||||
msgstr ""
|
||||
|
||||
msgid "Service for determining the public IP address through DNS."
|
||||
msgid "Service for determining the public IP address."
|
||||
msgstr ""
|
||||
|
||||
msgid "Service: detector always runs as a system service."
|
||||
@@ -477,6 +504,12 @@ msgstr ""
|
||||
msgid "TCP port connection"
|
||||
msgstr ""
|
||||
|
||||
msgid "Telegram bot API token."
|
||||
msgstr ""
|
||||
|
||||
msgid "Telegram message will be sent when connected or disconnected from the Internet."
|
||||
msgstr ""
|
||||
|
||||
msgid "The type of record requested in the DNS query (if the service supports it)."
|
||||
msgstr ""
|
||||
|
||||
@@ -489,6 +522,9 @@ msgstr ""
|
||||
msgid "Timeout between stopping and starting a ModemManger interface when restarting."
|
||||
msgstr ""
|
||||
|
||||
msgid "To support HTTP services you need to install curl."
|
||||
msgstr ""
|
||||
|
||||
msgid "Type a time string"
|
||||
msgstr ""
|
||||
|
||||
@@ -525,12 +561,19 @@ msgstr ""
|
||||
msgid "When email will be sent"
|
||||
msgstr ""
|
||||
|
||||
msgid "When message will be sent"
|
||||
msgstr ""
|
||||
|
||||
msgid "Windows: 32 bytes"
|
||||
msgstr ""
|
||||
|
||||
msgid "Write messages to the system log."
|
||||
msgstr ""
|
||||
|
||||
msgid ""
|
||||
"You need to register a new %sTelegram bot%s. Then get the bot's API token and paste it into the <code>Bot token</code> field. After that, open a chat with the bot, write something (in the Telegram app) and you will be able to get the chat ID using the <code>ID</code> button."
|
||||
msgstr ""
|
||||
|
||||
msgid "after connection"
|
||||
msgstr ""
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ local appName = "internet-detector"
|
||||
local appExec = "/usr/bin/internet-detector"
|
||||
local mailsendExec = "/usr/bin/mailsend"
|
||||
local modemManagerInit = "/etc/init.d/modemmanager"
|
||||
local curlExec = "/usr/bin/curl"
|
||||
|
||||
local InternetDetector = require(appName .. ".main")
|
||||
local uci = require("uci")
|
||||
@@ -37,6 +38,16 @@ local function init()
|
||||
else
|
||||
lines[#lines + 1] = '"email_mod":false'
|
||||
end
|
||||
if prequire(appName .. ".modules.mod_telegram") then
|
||||
lines[#lines + 1] = '"telegram":true'
|
||||
else
|
||||
lines[#lines + 1] = '"telegram":false'
|
||||
end
|
||||
if unistd.access(curlExec, "x") then
|
||||
lines[#lines + 1] = '"curl_exec":true'
|
||||
else
|
||||
lines[#lines + 1] = '"curl_exec":false'
|
||||
end
|
||||
return string.format("{%s}", table.concat(lines, ","))
|
||||
end
|
||||
|
||||
|
||||
BIN
screenshots/06.jpg
Normal file
BIN
screenshots/06.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 166 KiB |
Reference in New Issue
Block a user