diff options
author | Vsevolod Stakhov <vsevolod@highsecure.ru> | 2019-11-04 17:53:58 +0000 |
---|---|---|
committer | Vsevolod Stakhov <vsevolod@highsecure.ru> | 2019-11-04 17:53:58 +0000 |
commit | e4ac34be79aa9bbbfe2926e37f443c32221cbfe0 (patch) | |
tree | a55e60e0e9296065a5d59a2039e6e9fc11c58ff4 /lualib | |
parent | 503d1bceb2df4b88acd41455ddebf0efae9fd391 (diff) | |
download | rspamd-e4ac34be79aa9bbbfe2926e37f443c32221cbfe0.tar.gz rspamd-e4ac34be79aa9bbbfe2926e37f443c32221cbfe0.zip |
[Feature] Add verdict library in lua
Diffstat (limited to 'lualib')
-rw-r--r-- | lualib/lua_bayes_learn.lua | 10 | ||||
-rw-r--r-- | lualib/lua_util.lua | 30 | ||||
-rw-r--r-- | lualib/lua_verdict.lua | 189 |
3 files changed, 199 insertions, 30 deletions
diff --git a/lualib/lua_bayes_learn.lua b/lualib/lua_bayes_learn.lua index 066e86a4d..ae8d901f8 100644 --- a/lualib/lua_bayes_learn.lua +++ b/lualib/lua_bayes_learn.lua @@ -17,7 +17,7 @@ limitations under the License. -- This file contains functions to simplify bayes classifier auto-learning local lua_util = require "lua_util" - +local lua_verdict = require "lua_verdict" local N = "lua_bayes" local exports = {} @@ -76,7 +76,7 @@ exports.autolearn = function(task, conf) end -- We have autolearn config so let's figure out what is requested - local verdict,score = lua_util.get_task_verdict(task) + local verdict,score = lua_verdict.get_specific_verdict("bayes", task) local learn_spam,learn_ham = false, false if verdict == 'passthrough' then @@ -98,6 +98,12 @@ exports.autolearn = function(task, conf) learn_ham = true end end + elseif conf.learn_verdict then + if verdict == 'spam' or verdict == 'junk' then + learn_spam = true + elseif verdict == 'ham' then + learn_ham = true + end end if conf.check_balance then diff --git a/lualib/lua_util.lua b/lualib/lua_util.lua index 842f079b2..bda8b0c02 100644 --- a/lualib/lua_util.lua +++ b/lualib/lua_util.lua @@ -1026,35 +1026,9 @@ end -- * `uncertain`: all other cases --]] exports.get_task_verdict = function(task) - local result = task:get_metric_result() + local lua_verdict = require "lua_verdict" - if result then - - if result.passthrough then - return 'passthrough',nil - end - - local score = result.score - - local action = result.action - - if action == 'reject' and result.npositive > 1 then - return 'spam',score - elseif action == 'no action' then - if score < 0 or result.nnegative > 3 then - return 'ham',score - end - else - -- All colors of junk - if action == 'add header' or action == 'rewrite subject' then - if result.npositive > 2 then - return 'junk',score - end - end - end - - return 'uncertain',score - end + return lua_verdict.get_default_verdict(task) end ---[[[ diff --git a/lualib/lua_verdict.lua b/lualib/lua_verdict.lua new file mode 100644 index 000000000..d6a1634a7 --- /dev/null +++ b/lualib/lua_verdict.lua @@ -0,0 +1,189 @@ +--[[ +Copyright (c) 2019, Vsevolod Stakhov <vsevolod@highsecure.ru> + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +]]-- + +local exports = {} + +---[[[ +-- @function lua_verdict.get_default_verdict(task) +-- Returns verdict for a task + score if certain, must be called from idempotent filters only +-- Returns string: +-- * `spam`: if message have over reject threshold and has more than one positive rule +-- * `junk`: if a message has between score between [add_header/rewrite subject] to reject thresholds and has more than two positive rules +-- * `passthrough`: if a message has been passed through some short-circuit rule +-- * `ham`: if a message has overall score below junk level **and** more than three negative rule, or negative total score +-- * `uncertain`: all other cases +--]] +local function default_verdict_function(task) + local result = task:get_metric_result() + + if result then + + if result.passthrough then + return 'passthrough',nil + end + + local score = result.score + + local action = result.action + + if action == 'reject' and result.npositive > 1 then + return 'spam',score + elseif action == 'no action' then + if score < 0 or result.nnegative > 3 then + return 'ham',score + end + else + -- All colors of junk + if action == 'add header' or action == 'rewrite subject' then + if result.npositive > 2 then + return 'junk',score + end + end + end + + return 'uncertain',score + end +end + +local default_possible_verdicts = { + passthrough = { + can_learn = false, + description = 'message has passthrough result', + }, + spam = { + can_learn = 'spam', + description = 'message is likely spam', + }, + junk = { + can_learn = 'spam', + description = 'message is likely possible spam', + }, + ham = { + can_learn = 'ham', + description = 'message is likely ham', + }, + uncertain = { + can_learn = false, + description = 'not certainity in verdict' + } +} + +-- Verdict functions specific for modules +local specific_verdicts = { + default = { + callback = default_verdict_function, + possible_verdicts = default_possible_verdicts + } +} + +local default_verdict = specific_verdicts.default + +exports.get_default_verdict = default_verdict.callback +exports.set_verdict_function = function(func, what) + assert(type(func) == 'function') + if not what then + -- Default verdict + local existing = specific_verdicts.default.callback + specific_verdicts.default.callback = func + exports.get_default_verdict = func + + return existing + else + local existing = specific_verdicts[what] + + if not existing then + specific_verdicts[what] = { + callback = func, + possible_verdicts = default_possible_verdicts + } + else + existing = existing.callback + end + + specific_verdicts[what].callback = func + return existing + end +end + +exports.set_verdict_table = function(verdict_tbl, what) + assert(type(verdict_tbl) == 'table' and + type(verdict_tbl.callback) == 'function' and + type(verdict_tbl.possible_verdicts) == 'table') + + if not what then + -- Default verdict + local existing = specific_verdicts.default + specific_verdicts.default = verdict_tbl + exports.get_default_verdict = specific_verdicts.default.callback + + return existing + else + local existing = specific_verdicts[what] + specific_verdicts[what] = verdict_tbl + return existing + end +end + +exports.get_specific_verdict = function(what, task) + if specific_verdicts[what] then + return specific_verdicts[what].callback(task) + end + + return exports.get_default_verdict(task) +end + +exports.get_possible_verdicts = function(what) + local lua_util = require "lua_util" + if what then + if specific_verdicts[what] then + return lua_util.keys(specific_verdicts[what].possible_verdicts) + end + else + return lua_util.keys(specific_verdicts.default.possible_verdicts) + end + + return nil +end + +exports.can_learn = function(verdict, what) + if what then + if specific_verdicts[what] and specific_verdicts[what].possible_verdicts[verdict] then + return specific_verdicts[what].possible_verdicts[verdict].can_learn + end + else + if specific_verdicts.default.possible_verdicts[verdict] then + return specific_verdicts.default.possible_verdicts[verdict].can_learn + end + end + + return nil -- To distinguish from `false` that could happen in can_learn +end + +exports.describe = function(verdict, what) + if what then + if specific_verdicts[what] and specific_verdicts[what].possible_verdicts[verdict] then + return specific_verdicts[what].possible_verdicts[verdict].description + end + else + if specific_verdicts.default.possible_verdicts[verdict] then + return specific_verdicts.default.possible_verdicts[verdict].description + end + end + + return nil +end + +return exports
\ No newline at end of file |