%%%-----------------------------------
%%% module  : lib_chat
%%% @author  : xyao
%%% email   : jiexiaowen@gmail.com
%%% Created : 2011.07.22
%%% description: 聊天
%%%-----------------------------------
-module(lib_chat).

%% 聊天
-export([
        send_text_msg/6,
        send_msg/6,
        get_chat_vip_count/3,
        get_chat_count_daily/1,
        check_talk_text_condition/5,
        check_talk_condition/4,
        check_talk_args/2,
        check_talk_lim/4,
        get_time/1,
        put_time/2,
        send_channel_left_count/2,
        send_error_code/2
        ,server_chat/7
       ]).

%% 传闻
-export([send_TV/4, make_tv/3]).

-export([
        update_sys_notice_4_houtai/0,
        forbid_chat_4_houtai/2,
        release_chat_4_houtai/1,
        forbid_chat/3,
        release_chat/2,
        get_talk_lim/1,
        be_lim_talk/1,
        chat_too_frequently/2,
%%         send_sys_msg/2,
        is_chat_forbid/1,
        forbid_chat_db/4,
        log_forbid_chat/3       %% 禁言日志
]).

-export([clear_talk_reported_count/1]).

-compile(export_all).

-include("unite.hrl").
-include("server.hrl").
-include("chat.hrl").
-include("figure.hrl").
-include("team.hrl").
-include("def_module.hrl").
-include("def_daily.hrl").
-include("common.hrl").
-include("guild.hrl").
-include("def_vip.hrl").
-include("goods.hrl").
-include("def_event.hrl").
-include("rec_event.hrl").
-include("errcode.hrl").
-include("dungeon_multi.hrl").
-include("def_goods.hrl").
-include("game_vsn.hrl").
-include("role.hrl").
-include("scene.hrl").
-include("language.hrl").
%% ----------------------------------------------------------------
%% 文字聊天
%% ----------------------------------------------------------------
chat(Status, Channel, ReceiveId, Msg, ArgsStr, TkTime, Ticket, IsSystemMsg) ->
    #player_status{server_id = SerId, server_num = SerNum, server_name = ServerName, id = Id, figure = Figure, sid = Sid, talk_lim_type = TalkLimType} = Status,
    #figure{gm = GM, lv = Lv, name = _Name} = Figure,
    %% 检查是否能聊天
    case lib_chat:check_talk_text_condition(Status, Channel, TkTime, Ticket, Msg) of
        {fail, ErrorCode} -> lib_chat:send_error_code(Sid, ErrorCode);
        true ->
            [Count, Status1] = lib_chat:record_content(Status, Channel, Msg),  %% TODO:一分钟内相同内容监测
            if
                Count == 1 ->
                    lib_chat:send_error_code(Sid, ?ERRCODE(err110_content_repeat)),
                    {ok, Status1};
                true ->
                    MsgSend = case GM of
                                  1 -> lib_word:filter_text_gm(Msg);
                                  2 -> lib_word:filter_text_gm(Msg);
                                  _ -> lib_word:filter_text(Msg, Lv)
                              end,
                    NewArgs = case util:string_to_term(ArgsStr) of
                                  undefined -> [];
                                  Args -> Args
                              end,
                    %% 参数检测
                    ArgsResult = lib_chat:check_talk_args(Status1, NewArgs),
                    %% 检查聊天禁言
                    IsTalkLim = lib_chat:check_talk_lim(Status1, Channel, TkTime, Ticket),

                    NowTime = utime:unixtime(),
                    %% 通用私聊(只要不是黑名单关系就可私聊,包括陌生人)，
                    %% 具体逻辑由do_send_msg控制
                    ChatTo =
                        if
                            Channel == ?CHAT_CHANNEL_PRIVATE ->
                                case lib_role:get_role_show(ReceiveId) of
                                    [] -> ReceiveFigure = #figure{};
                                    #ets_role_show{figure = ReceiveFigure} -> ok
                                end,
                                PackData = [Channel, SerId, SerNum, ServerName, [{Id, Figure}, {ReceiveId, ReceiveFigure}], MsgSend, util:term_to_string(NewArgs), ArgsResult, NowTime],
                                {ok, BinData} = pt_110:write(11002, PackData),
                                #figure{
                                    name = ChatToName
                                    , accname = ChatToAccName
                                    , lv = ChatToLevel
                                    , vip = ChatToVipLevel
                                    , power = ChatToPower
                                    , career = ChatToCareer
                                } = ReceiveFigure,
                                #chat_to{
                                    id = ReceiveId
                                    , name = ChatToName
                                    , level = ChatToLevel
                                    , vip_level = ChatToVipLevel
                                    , accname = ChatToAccName
                                    , power = ChatToPower
                                    , career = ChatToCareer
                                };
                            Channel == ?CHAT_CHANNEL_WORLD_KF orelse  Channel == ?CHAT_CHANNEL_COUNTRY ->
                                PackData = [Channel, SerId, SerNum, ServerName, Id, Figure, MsgSend, util:term_to_string(NewArgs), ArgsResult, NowTime],
                                BinData = 0,
                                #chat_to{};
                            true ->
                                PackData = [Channel, SerId, SerNum, ServerName, Id, Figure, MsgSend, util:term_to_string(NewArgs), ArgsResult, NowTime],
                                PackData2 = [Channel, SerId, SerNum, ServerName, Id, Figure, MsgSend, util:term_to_string(NewArgs), ArgsResult, NowTime, 0, 0],
                                BinData = make_data(PackData2),
                                #chat_to{}
                        end,
                    if
                        IsTalkLim andalso (Channel == ?CHAT_CHANNEL_WORLD_KF orelse  Channel == ?CHAT_CHANNEL_COUNTRY) -> mod_chat_agent:send_msg_to_kf(self, Id, PackData);
                        IsTalkLim andalso TalkLimType /= ?TALK_LIMIT_TYPE_6 -> lib_server_send:send_to_sid(Sid, BinData);
                        GM == 1 orelse GM == 2 ->
                            lib_chat:send_msg(?CHAT_CONTENT_TEXT, Channel, Status1, ReceiveId, PackData, BinData);
                        true ->
                            case pp_chat:check_auto_banned(Id, Msg, MsgSend) of
                                true ->
                                    {ok, NewStatus} = lib_chat:send_text_msg(Status1, ?CHAT_CONTENT_TEXT, Channel, ReceiveId, PackData, BinData),
                                    %% 聊天监控
                                    case data_vsn:is_chat_monitor() of
                                        true when IsSystemMsg == 0 ->
                                            chat_monitor_agent:role_chat(Status1, Channel, Msg, utime:unixtime(), ChatTo);
                                        _ -> skip
                                    end,
                                    {ok, NewStatus};
                                false -> {ok, Status1}
                            end
                    end
            end
    end.

%% 服务端直接发起聊天信息，目的是为了绕过cd和一些复杂检测。
%% 服务端内部调用，谨慎使用。
server_chat(Status, Channel, ReceiveId, Msg, ArgsStr, TkTime, Ticket) ->
    #player_status{server_id = SerId, server_num = SerNum, server_name = ServerName, id = Id, figure = Figure, sid = Sid, talk_lim_type = TalkLimType} = Status,
    #figure{gm = GM, lv = Lv, name = _Name} = Figure,
    NewArgs = case util:string_to_term(ArgsStr) of
                undefined -> [];
                Args -> Args
            end,
    MsgSend = case GM of
                  1 -> lib_word:filter_text_gm(Msg);
                  2 -> lib_word:filter_text_gm(Msg);
                  _ -> lib_word:filter_text(Msg, Lv)
              end,
    %% 参数检测
    ArgsResult = lib_chat:check_talk_args(Status, NewArgs),
    %% 检查聊天禁言
    IsTalkLim = lib_chat:check_talk_lim(Status, Channel, TkTime, Ticket),

    NowTime = utime:unixtime(),
    %% 通用私聊(只要不是黑名单关系就可私聊,包括陌生人)，
    %% 具体逻辑由do_send_msg控制
    if
        Channel == ?CHAT_CHANNEL_PRIVATE ->
            case lib_role:get_role_show(ReceiveId) of
                [] -> ReceiveFigure = #figure{};
                #ets_role_show{figure = ReceiveFigure} -> ok
            end,
            PackData = [Channel, SerId, SerNum, ServerName, [{Id, Figure}, {ReceiveId, ReceiveFigure}], MsgSend, util:term_to_string(NewArgs), ArgsResult, NowTime],
            {ok, BinData} = pt_110:write(11002, PackData);
        Channel == ?CHAT_CHANNEL_WORLD_KF orelse  Channel == ?CHAT_CHANNEL_COUNTRY ->
            PackData = [Channel, SerId, SerNum, ServerName, Id, Figure, MsgSend, util:term_to_string(NewArgs), ArgsResult, NowTime],
            BinData = 0;
        true ->
            PackData = [Channel, SerId, SerNum, ServerName, Id, Figure, MsgSend, util:term_to_string(NewArgs), ArgsResult, NowTime],
            PackData2 = [Channel, SerId, SerNum, ServerName, Id, Figure, MsgSend, util:term_to_string(NewArgs), ArgsResult, NowTime, 0, 0],
            BinData = make_data(PackData2)
    end,
    if
        IsTalkLim andalso (Channel == ?CHAT_CHANNEL_WORLD_KF orelse  Channel == ?CHAT_CHANNEL_COUNTRY) -> mod_chat_agent:send_msg_to_kf(self, Id, PackData);
        IsTalkLim andalso TalkLimType /= ?TALK_LIMIT_TYPE_6 -> lib_server_send:send_to_sid(Sid, BinData);
        GM == 1 orelse GM == 2 ->
            lib_chat:send_msg(?CHAT_CONTENT_TEXT, Channel, Status, ReceiveId, PackData, BinData);
        true ->
            case pp_chat:check_auto_banned(Id, Msg, MsgSend) of
                true ->
                    {ok, NewStatus} = lib_chat:send_text_msg(Status, ?CHAT_CONTENT_TEXT, Channel, ReceiveId, PackData, BinData),
                    %% 聊天监控(服务端自己发的，一般是策划配置的活动发言，就不要监控了)
                    % case data_vsn:is_chat_monitor() of
                    %    true ->
                    %        chat_monitor_agent:role_chat(Status, Channel, Msg, utime:unixtime(), ChatTo);
                    %    false -> skip
                    % end,
                    {ok, NewStatus};
                false ->
                    {ok, Status}
            end
    end.

make_data(PackData) ->
    {ok, BinData} = pt_110:write(11001, PackData),
    BinData.
%% ----------------------------------------------------------------
%% 接口
%% ----------------------------------------------------------------

send_voice_text(Status, Channel, ReceiveId, ClientAutoId, VoiceTextData) ->
    {ok, BinData} = pt_110:write(11013, [Channel, ClientAutoId, VoiceTextData]),
    send_msg_on_direct(Status, Channel, ReceiveId, BinData).

%% 直接发送消息
send_msg_on_direct(Status, Channel, ReceiveId, BinData) ->
    #player_status{
        id = Id, figure = Figure, scene = Scene, scene_pool_id = ScenePoolId, copy_id = CopyId,
        sid = Sid, team = #status_team{team_id = TeamId}, x = X, y = Y, guild = Guild,
        dun_multi = #role_dun_multi{team_id = MulTeamId}
    } = Status,
    #figure{career = _Career} = Figure,
    case Channel of
        ?CHAT_CHANNEL_WORLD ->
            lib_achievement_api:world_chat_event(Status, 1),
            lib_server_send:send_to_all(BinData);
        ?CHAT_CHANNEL_HORN -> skip;
        ?CHAT_CHANNEL_NEARBY ->
            lib_server_send:send_to_area_scene(Scene, ScenePoolId, CopyId, X, Y, BinData);
        ?CHAT_CHANNEL_GUILD when Guild#status_guild.id > 0 ->
            lib_achievement_api:guild_chat_event(Status, 1),
            lib_seven_days_goal:finish_task(Id, ?MOD_CHAT, ?CHAT_CHANNEL_GUILD, 1),
            lib_server_send:send_to_guild(Guild#status_guild.id, BinData);
        ?CHAT_CHANNEL_TEAM when TeamId > 0 ->
            lib_team_api:send_to_team(TeamId, BinData);
        ?CHAT_CHANNEL_TEAM when MulTeamId > 0 ->
            lib_dun_multi_cluster:send_to_team(MulTeamId, BinData);
        ?CHAT_CHANNEL_PRIVATE ->
            lib_achievement_api:private_chat_event(Status, 1),
            lib_server_send:send_to_sid(Sid, BinData),
            lib_server_send:send_to_uid(ReceiveId, BinData);
        ?CHAT_CHANNEL_RELA ->
            lib_server_send:send_to_sid(Sid, BinData),
            lib_server_send:send_to_rela(Id, BinData);
%%        ?CHAT_CHANNEL_REPLY -> lib_server_send:send_to_scene(Scene, ScenePoolId, BinData);
        ?CHAT_CHANNEL_3V3_TEAM -> lib_3v3_api:send_to_3v3_team(Id, BinData);
        ?CHAT_CHANNEL_1VN_BET -> lib_kf_1vn:send_to_area(Id, BinData);
        %% ?CHAT_CHANNEL_CAREER -> lib_server_send:send_to_career(Career, BinData);
        _ -> lib_server_send:send_to_sid(Sid, BinData)
    end.

%% ----------------------------------------------------------------
%% pp_chat 聊天
%% ----------------------------------------------------------------

send_text_msg(Status, ChatContent, Channel, ReceiveId, PackData, BinData) ->
    send_msg(ChatContent, Channel, Status, ReceiveId, PackData, BinData).

%% 根据不同的聊天类型发送不同的消息(发送语音、图片、文本)
%% @param PackData 缓存,号角等数据
%% @param BinData 
%% @return
send_msg(ChatContent, Channel, Status, ReceiveId, PackData, BinData) ->
    #player_status{id = Id, sid = Sid, talk_lim = _TalkLim} = Status,
    case Channel of
        ?CHAT_CHANNEL_PRIVATE ->
            [_, _, _, _, _, MsgSend | _] = PackData;
        _ ->
            [_, _, _, _, _, _, MsgSend | _] = PackData
    end,

    case check_send_msg(Status, Channel, _Extra = [ReceiveId, MsgSend]) of
        true ->
            NewStatus = Status,
            do_send_msg(ChatContent, Channel, NewStatus, ReceiveId, PackData, BinData);
        {true, daily} ->
            NewStatus = Status,
            increment_chat_count(Channel, Id),
            do_send_msg(ChatContent, Channel, NewStatus, ReceiveId, PackData, BinData);
        {true, vip} ->
            NewStatus = Status,
            increment_chat_count(Channel, Id),
            do_send_msg(ChatContent, Channel, NewStatus, ReceiveId, PackData, BinData);
        {true, consume, Cost} ->
            case lib_goods_api:cost_object_list(Status, Cost, chat, lists:concat([Channel])) of
                {true, NewStatus} ->
                    % increment_chat_count(Channel, Id),
                    do_send_msg(ChatContent, Channel, NewStatus, ReceiveId, PackData, BinData);
                {false, _ErrorCode, NewStatus} -> lib_server_send:send_to_sid(Sid, BinData)
            end;
        {false, ErrorCode} ->
            send_error_code(Sid, ErrorCode),
            NewStatus = Status;
%%        Res = [_|_] ->
%%            IsNumLimit = lists:member({num_limit}, Res),
%%            StringLimit = lists:member({string_limit}, Res),
%%            ChatNumLimit = lists:member({true, chat_count_incre}, Res),
%%            IsPrivateLimit = lists:member({lv_power_diff_limit}, Res),
%%            IsSensitive = lists:member({sensisitve_limit}, Res),
%%            IsOppTalkLim = lists:member({opp_talk_lim}, Res),
%%            %% 下面判断中越靠前优先度越高
%%            % case Channel of
%%            %     ?CHAT_CHANNEL_WORLD -> %% 次数&长度&敏感词
%%            if
%%                IsNumLimit =:= true ->%% 等级不够次数已用完
%%                    lib_server_send:send_to_sid(Sid, BinData),
%%                    NewStatus = Status;
%%                IsPrivateLimit =:= true -> %% 私聊双方等级过大
%%                    lib_server_send:send_to_sid(Sid, BinData),
%%                    NewStatus = Status;
%%                IsOppTalkLim =:= true ->
%%                    % send_error_code(Sid, ?ERR110_TALK_LIMIT),
%%                    lib_server_send:send_to_sid(Sid, BinData),
%%                    NewStatus = Status;
%%                IsSensitive =:= true ->
%%                    if TalkLim =:= 1 -> skip;
%%                        true ->
%%                            lib_server_send:send_to_sid(Sid, BinData),
%%                            lib_chat:forbid_chat([Id], 0, ?TALK_LIMIT_TYPE_2) %% 禁言300秒TALK_LIMIT_TIME_0 测试
%%                    end,
%%                    NewStatus = Status;
%%                StringLimit andalso ChatNumLimit -> %% 长度检测和次数更新
%%                    case util:check_length(BinData, ?DIFF_STRING) of
%%                        true ->
%%                            NewStatus = lib_chat_limit:incre_lv_chat_count_limit(Status),
%%                            do_send_msg(ChatContent, Channel, NewStatus, ReceiveId, PackData, BinData);
%%                        _    ->
%%                            NewStatus = Status,
%%                            lib_server_send:send_to_sid(Sid, BinData)
%%                    end;
%%                StringLimit -> %% 检查长度
%%                    case util:check_length(BinData, ?DIFF_STRING) of
%%                        true ->
%%                            do_send_msg(ChatContent, Channel, Status, ReceiveId, PackData, BinData);
%%                        _    ->
%%                            lib_server_send:send_to_sid(Sid, BinData)
%%                    end,
%%                    NewStatus = Status;
%%                ChatNumLimit -> %% 只更新次数
%%                    NewStatus = lib_chat_limit:incre_lv_chat_count_limit(Status),
%%                    do_send_msg(ChatContent, Channel, NewStatus, ReceiveId, PackData, BinData);
%%                true ->
%%                    NewStatus = Status,
%%                    do_send_msg(ChatContent, Channel, NewStatus, ReceiveId, PackData, BinData)
%%            end;
        _ ->
            NewStatus = Status
    end,
    {ok, NewStatus}.

do_send_msg(ChatContent, Channel, Status, ReceiveId, PackData, BinData) ->
    #player_status{
        id = Id, figure = _Figure, scene = Scene, scene_pool_id = ScenePoolId,
        copy_id = CopyId, sid = Sid, team = #status_team{team_id = TeamId},
        x = _X, y = _Y, guild = Guild, dun_multi = #role_dun_multi{team_id = MulTeamId}
    } = Status,
    case Channel of
        ?CHAT_CHANNEL_WORLD ->
            case ChatContent == ?CHAT_CONTENT_TEXT of
                true ->
                    %% 触发成就
                    lib_achievement_api:async_event(Id, lib_achievement_api, world_chat_event, 1),
                    contract_task_api:world_chat_trigger(Id, 1),
                    lib_player_event:async_dispatch(Id, ?EVENT_CHAT_WORLD_TEXT);
                false -> skip
            end,
            mod_chat_cache:save_cache(Channel, Id, ReceiveId, PackData),
            lib_server_send:send_to_all(BinData);
        %% 只有文字有气泡（喇叭）
        ?CHAT_CHANNEL_HORN ->
            lib_player_event:async_dispatch(Id, ?EVENT_CHAT_HORN),
            case ChatContent == ?CHAT_CONTENT_TEXT of
                true -> mod_chat_bugle:put(#bugle{role_id = Id, show_time = ?CHAT_HORN_SHOW_TIME, msg = PackData});
                false -> skip
            end;
        ?CHAT_CHANNEL_NEARBY ->
            lib_server_send:send_to_scene(Scene, ScenePoolId, CopyId, BinData);
        ?CHAT_CHANNEL_GUILD when Guild#status_guild.id > 0 ->
            %% 触发成就
            lib_achievement_api:async_event(Id, lib_achievement_api, guild_chat_event, 1),
            lib_seven_days_goal:finish_task(Id, ?MOD_CHAT, ?CHAT_CHANNEL_GUILD, 1),
            mod_chat_cache:save_cache({Channel, Guild#status_guild.id}, Id, ReceiveId, PackData),
            lib_server_send:send_to_guild(Guild#status_guild.id, BinData);
        ?CHAT_CHANNEL_TEAM when TeamId > 0 -> lib_team_api:send_to_team(TeamId, BinData);
        ?CHAT_CHANNEL_TEAM when MulTeamId > 0 -> lib_dun_multi_cluster:send_to_team(MulTeamId, BinData);
        ?CHAT_CHANNEL_PRIVATE ->
            lib_server_send:send_to_sid(Sid, BinData),
            case mod_chat_agent:lookup(ReceiveId) of
                [#ets_unite{online = 1}] -> IsRealOnline = 1;
                _ -> IsRealOnline = 0
            end,
            %% 黑名单不发送
            IsBlack1 = lib_chat_limit:is_blocked(Status, ReceiveId),
            if IsBlack1 ->
                lib_server_send:send_to_sid(Sid, pt_110, 11000, [?ERRCODE(err140_30_in_blacklist)]);
                true ->
                    %% 触发成就
                    lib_achievement_api:async_event(Id, lib_achievement_api, private_chat_event, 1),
                    lib_server_send:send_to_uid(ReceiveId, BinData),
                    if
                        IsRealOnline == 1 ->
                            skip;
                        true -> %% 只有满足一定的亲密度才能进行离线聊天
                            [{_, _, Intimacy} | _] = lib_relationship:get_rela_and_intimacy_online(Id, [ReceiveId]),
                            % if Intimacy >= ?INTIMACY_LIMIT ->
                            if Intimacy >= 0 ->
                                mod_chat_cache:save_cache(?CHAT_CHANNEL_PRIVATE, Id, ReceiveId, PackData);
                                true ->
                                    lib_server_send:send_to_sid(Sid, pt_110, 11000, [?ERRCODE(err110_intimacy_limit)])
                            end
                    % (ChatContent == ?CHAT_CONTENT_TEXT) orelse (ChatContent == ?CHAT_CONTENT_VOICE andalso PackData =/= []) ->
                    %     case lib_player:is_id_exists(ReceiveId) of
                    %         true -> mod_chat_cache:save_cache(Channel, Id, ReceiveId, PackData);
                    %         false -> skip
                    %     end;
                    % true ->
                    %     lib_server_send:send_to_sid(Sid, pt_110, 11000, [?ERRCODE(err140_14_not_online)])
                    end,
                    lib_player_event:dispatch(Status, ?EVENT_CHAT_PRIVATE, ReceiveId)
            end;
        ?CHAT_CHANNEL_RELA ->
            lib_server_send:send_to_sid(Sid, BinData),
            lib_server_send:send_to_rela(Id, BinData);
        ?CHAT_CHANNEL_WAIT_TEAM ->
            lib_server_send:send_to_all(all_lv, {?CHAT_LV_TEAM, 65535}, BinData);
        ?CHAT_CHANNEL_SCENE ->
            lib_server_send:send_to_scene(Scene, ScenePoolId, CopyId, BinData);
        ?CHAT_CHANNEL_WORLD_KF ->
            mod_chat_agent:send_msg_to_kf(all, Id, PackData);
        ?CHAT_CHANNEL_COUNTRY ->
            mod_chat_agent:send_msg_to_kf(all, Id, PackData);
        % ?CHAT_CHANNEL_REPLY ->
        %   lib_server_send:send_to_scene(Scene, ScenePoolId, BinData);
        ?CHAT_CHANNEL_3V3_TEAM ->
            lib_3v3_api:send_to_3v3_team(Id, BinData);
        ?CHAT_CHANNEL_1VN_BET ->
            lib_kf_1vn:send_to_area(Id, BinData);
        ?CHAT_CHANNEL_BEACH ->     %% 温泉
            case data_scene:get(Scene) of
                #ets_scene{cls_type = ?SCENE_CLS_TYPE_GAME} ->
                    mod_chat_agent:send_msg([{scene, Scene, ScenePoolId, BinData}]);
                #ets_scene{cls_type = ?SCENE_CLS_TYPE_CENTER} ->
                    ServerId = config:get_server_id(),
                    clusters_node:apply_cast(?MODULE, do_send_clusters_msg, [{{channel, ?CHAT_CHANNEL_BEACH}, ServerId, Scene, ScenePoolId, BinData}]);
                _ ->
                    skip
            end;
        ?CHAT_CHANNEL_GLAMOUR ->
            lib_server_send:send_to_all(BinData);
        ?CHAT_CHANNEL_GUARD_RECRUIT ->
            ServerId = config:get_server_id(),
            clusters_node:apply_cast(?MODULE, do_send_clusters_msg, [{{channel, Channel}, ServerId, BinData}]);
        _ ->
            lib_server_send:send_to_sid(Sid, BinData)
    end.

%% 检查是否能发送(次数,消耗物品)
%% true | {true, daily}(消耗日常次数) | {true, consume, Consume}(消耗物品) | {false, ErrorCode}
check_send_msg(Status, Channel, _) when Channel == ?CHAT_CHANNEL_HORN ->
    #player_status{id = _Id, figure = Figure} = Status,
    #figure{vip = _Vip, gm = GM} = Figure,
    if
        GM == 1 -> true;
        GM == 2 -> true;
    %% ChatCount > Count -> {true, daily};
        true ->
            case get_cost(Channel) of
                [] -> {true, consume, []};
                Cost ->
                    case lib_goods_api:check_object_list(Status, Cost) of
                        {false, ErrorCode} -> {false, ErrorCode};
                        true -> {true, consume, Cost}
                    end
            end
    end;
%%%% 世界聊天检测等级次数限制/等级对应字符串长度限制/敏感词检测
%%check_send_msg(Status, Channel, Data) when Channel == ?CHAT_CHANNEL_WORLD ->
%%    GameVersion = data_vsn:get_game_vsn(),
%%    CheckList = if GameVersion =:= ?VER_TEST orelse GameVersion =:= ?VER_MAIN ->
%%            [num_limit, lv_diff_string_limit];
%%        true ->
%%            []
%%    end,
%%    lib_chat_limit:check_chat_role(CheckList, Status, Data, _Res = []);
%% 私聊频道检测等级次数限制/私聊双方等级战力差/敏感词/对方是否被禁言
%%check_send_msg(Status, Channel, Data = [ReceiveId, _]) when Channel == ?CHAT_CHANNEL_PRIVATE ->
%%    GameVersion = data_vsn:get_game_vsn(),
%%    #player_status{ip = RoleIp} = Status,
%%    CheckList =
%%        if GameVersion =:= ?VER_TEST
%%            orelse GameVersion =:= ?VER_MAIN ->
%%            % 检测是否同ip,同ip则不检测
%%            case mod_chat_agent:lookup(ReceiveId) of
%%                [#ets_unite{ip = IpBin}] ->
%%                    if RoleIp =:= IpBin -> [];
%%                        true ->
%%                            [num_limit, lv_power_diff_limit, opp_talk_lim]
%%                    end;
%%                _ ->
%%                    [num_limit, lv_power_diff_limit, opp_talk_lim]
%%            end;
%%            GameVersion =:= ?VER_TW ->
%%                [{lv_power_diff_limit, GameVersion}];
%%            true ->
%%                []
%%        end,
%%    lib_chat_limit:check_chat_role(CheckList, Status, Data, _Res = []);
%% 跨服聊天检测
check_send_msg(_Status, ?CHAT_CHANNEL_WORLD_KF, _Data) ->
    true;
%%check_send_msg(Status, Channel, _Data) when Channel == ?CHAT_CHANNEL_WORLD_KF ->
%%    #player_status{id = RoleId, figure = #figure{vip_type = VipType, vip = Vip}} = Status,
%%    VipCount = get_chat_vip_count(Channel, VipType, Vip),
%%    GoodsCount = get_chat_goods_count(RoleId, Channel),
%%    DailyId = get_chat_count_daily(Channel),
%%    Count = lib_daily:get_count(RoleId, ?MOD_CHAT, DailyId),
%%    LeftVipCount = max(VipCount - Count, 0),
%%    LeftCount = LeftVipCount + GoodsCount,
%%    if
%%        LeftCount =< 0 -> {false, ?ERRCODE(err110_times_not_enough)};
%%        LeftVipCount > 0 -> {true, vip};
%%        true ->
%%            case get_cost(Channel) of
%%                [] -> {true, consume, []};
%%                Cost ->
%%                    case lib_goods_api:check_object_list(Status, Cost) of
%%                        {false, ErrorCode} -> {false, ErrorCode};
%%                        true -> {true, consume, Cost}
%%                    end
%%            end
%%    end;
check_send_msg(_Status, _Channel, _Extra) ->
    true.
check_send_msg(_Status, _Channel) ->
    true.

%% 获得聊天的vip次数
get_chat_vip_count(_Channel, _VipType, _Vip) -> 0.
% case Channel of
%     ?CHAT_CHANNEL_WORLD ->
%         lib_vip:get_vip_privilege(VipType, Vip, ?MOD_CHAT, ?NBV_CHANNEL_WORLD);
%     ?CHAT_CHANNEL_WORLD_KF ->
%         lib_vip:get_vip_privilege(VipType, Vip, ?MOD_CHAT, ?NBV_CHANNEL_WORLD_KF);
%     _ -> 0
% end.

%% 获得聊天次数日常
get_chat_count_daily(Channel) ->
    case Channel of
        ?CHAT_CHANNEL_WORLD -> ?DAILY_CHAT_WORLD;
        ?CHAT_CHANNEL_WORLD_KF -> ?DAILY_CHAT_WORLD_KF;
        _ -> 0
    end.

%% 获得物品次数
get_chat_goods_count(_RoleId, Channel) ->
    case Channel of
        ?CHAT_CHANNEL_WORLD_KF ->
            0;
        _ -> 0
    end.

%% 聊天消耗
get_cost(?CHAT_CHANNEL_HORN) -> [{?TYPE_GOODS, ?CHAT_GOODS_HORN, 1}];
get_cost(_Channel) -> [].

%% 增加聊天日常次数
increment_chat_count(Channel, Id) ->
    case get_chat_count_daily(Channel) of
        0 -> skip;
        DailyId -> lib_daily:increment(Id, ?MOD_CHAT, DailyId)
    end.

%% 检查是否能聊文字
check_talk_text_condition(Status, Channel, TkTime, Ticket, Data) ->
    case check_talk_condition(Status, Channel, TkTime, Ticket) of
        true ->
            Len = get_len(Channel),
            case util:check_length(Data, Len) of
                true -> true;
                false -> {fail, ?ERRCODE(err110_content_len)}
            end;
        ErrorRes ->
            ErrorRes
    end.

%% 检查是否能聊天(语音、图片、文本通用判断)
%% 检查是否能聊天(语音、图片、文本通用判断)
check_talk_condition(Status, Channel, _TkTime, _Ticket) ->
    #player_status{figure = Figure = #figure{vip = Vip}} = Status,
    #figure{gm = GM} = Figure,
    Now = utime:unixtime(),
    Time = get_time(Channel),       %% 上次发言时间
    CD = get_cd(Vip, Channel),    %% 各种聊天的cd限制
    IsOpen = lists:member(Channel, ?CHAT_CHANNEL_OPEN_LIST),
    NeedLv = get_lv(Channel),
    if
        GM == 1 -> true;
        GM == 2 -> true;
        Figure#figure.lv < NeedLv -> {fail, ?ERRCODE(err110_lv_limit)}; %% 等级不够
        IsOpen == false -> {fail, ?ERRCODE(err110_unknow_channel)};     %% 未知频道
        Now - Time < CD -> {fail, ?ERRCODE(err110_in_cd)};              %% cd未到
        true ->
            true
    end.

%%获取上次发言时间
get_time(Channel) ->
    case get({chat_channel, Channel}) of
        undefined -> 0;
        Time -> Time
    end.

clean_world_channel_cd() ->
    erase({chat_channel, ?CHAT_CHANNEL_WORLD}).

%% 获取不同类型的cd时间
get_cd(_Vip, Channel) ->
    case Channel of
        ?CHAT_CHANNEL_WORLD -> ?CHAT_GAP_WORLD;
        ?CHAT_CHANNEL_HORN -> ?CHAT_GAP_HORN;
        ?CHAT_CHANNEL_NEARBY -> ?CHAT_GAP_NEARBY;
        ?CHAT_CHANNEL_GUILD -> ?CHAT_GAP_GUILD;
        ?CHAT_CHANNEL_TEAM -> ?CHAT_GAP_TEAM;
        ?CHAT_CHANNEL_PRIVATE -> ?CHAT_GAP_PRIVATE;
        ?CHAT_CHANNEL_RELA -> ?CHAT_GAP_RELA;
        ?CHAT_CHANNEL_WAIT_TEAM -> ?CHAT_GAP_WAIT_TEAM;
        ?CHAT_CHANNEL_SCENE -> ?CHAT_GAP_SCENE;
        ?CHAT_CHANNEL_WORLD_KF -> ?CHAT_GAP_WORLD_KF;
        ?CHAT_CHANNEL_GUARD_RECRUIT -> ?CHAT_GAP_GUARD;
%%        ?CHAT_CHANNEL_REPLY     -> ?CHAT_GAP_REPLY;
        _ -> ?CHAT_GAP_DEFAULT
    end.

%% 聊天等级
get_lv(Channel) ->
    case Channel of
        ?CHAT_CHANNEL_WORLD -> ?CHAT_LV_WORLD;
        ?CHAT_CHANNEL_HORN -> ?CHAT_LV_HORN;
        ?CHAT_CHANNEL_NEARBY -> ?CHAT_LV_NEARBY;
        ?CHAT_CHANNEL_GUILD -> ?CHAT_LV_GUILD;
        ?CHAT_CHANNEL_TEAM -> ?CHAT_LV_TEAM;
        ?CHAT_CHANNEL_PRIVATE -> ?CHAT_LV_PRIVATE;
        ?CHAT_CHANNEL_RELA -> ?CHAT_LV_RELA;
        ?CHAT_CHANNEL_SCENE -> ?CHAT_LV_SCENE;
        ?CHAT_CHANNEL_WORLD_KF -> ?CHAT_LV_WORLD_KF;
        ?CHAT_CHANNEL_WEDDING -> ?CHAT_LV_WEDDING;
        _ -> ?CHAT_LV_DEFAULT
    end.

%% 长度
get_len(Channel) ->
    case Channel of
        ?CHAT_CHANNEL_WAIT_TEAM -> 1200;
        _ -> ?CHAT_LEN_DEFAULT
    end.

%% 检查聊天参数[比如装备展示等需要做验证或者过滤]
%% Args:
%%  {goods, GoodsTypeId(物品类型id), GoodsId(物品唯一id)}:校验物品
%%  {partner, PartnerId(伙伴id), PartnerAutoId(伙伴唯一id)}:校验伙伴
%% @return 0:无参数 1:有参数校验正常 2:有参数校验失败
check_talk_args(_Status, []) -> ?CHAT_ARGS_RESULT_NO;
check_talk_args(Status, Args) -> check_talk_args_help(Args, Status).

check_talk_args_help([], _Status) -> ?CHAT_ARGS_RESULT_PASS;
check_talk_args_help([{goods, GoodsTypeId, GoodsId} | L], Status) ->
    GoodsStatus = lib_goods_do:get_goods_status(),
    GoodsInfo = lib_goods_util:get_goods(GoodsId, GoodsStatus#goods_status.dict),
    case is_record(GoodsInfo, goods) of
        true when GoodsInfo#goods.player_id == GoodsStatus#goods_status.player_id andalso GoodsInfo#goods.goods_id == GoodsTypeId ->
            check_talk_args_help(L, Status);
        false ->
            ?CHAT_ARGS_RESULT_NO_PASS
    end;
check_talk_args_help(_Args, _Status) ->
    ?CHAT_ARGS_RESULT_NO_PASS.

%% 检查禁言（true:禁言，false:允许说话）- 注意:校验为禁言后把信息发给自己
check_talk_lim(Status, Channel, _TkTime, _Ticket) ->
    #player_status{
        id = Id,
        talk_lim = TalkLim,
        talk_lim_time = TalkLimTime
    } = Status,
    Now = utime:unixtime(),
    case Now < TalkLimTime of %% 是否被禁言
        true -> true;
        false when TalkLim == 1 ->
            release_chat([Id], ?TALK_RELEASE_TYPE_2),
            put_time(Channel, Now),
            false;
        false ->
            put_time(Channel, Now),
            false
    end.

%% 写入当前发言时间
put_time(Channel, Time) -> put({chat_channel, Channel}, Time).

%% 发送世界/跨服频道聊天剩余的次数，vip特权/消耗物品次数有叠加
%% 此接口目前基本上不使用
send_channel_left_count(Channel, Status) ->
    #player_status{id = RoleId, figure = #figure{vip_type = VipType, vip = VipLv}} = Status,
    VipCount = lib_chat:get_chat_vip_count(Channel, VipType, VipLv),
    GoodsCount = lib_chat:get_chat_goods_count(RoleId, Channel),
    DailyId = lib_chat:get_chat_count_daily(Channel),
    Count = lib_daily:get_count(RoleId, ?MOD_CHAT, DailyId),
    % LeftCount = max(VipCount + GoodsCount - Count, 0),
    LeftVipCount = max(VipCount - Count, 0),
    LeftCount = LeftVipCount + GoodsCount,
    {ok, BinData} = pt_110:write(11011, [LeftCount]),
    lib_server_send:send_to_sid(Status#player_status.sid, BinData).

%% 发送小红点
send_red_point(Status, ModuleId, Type) ->
    if
        ModuleId == ?MOD_GUILD ->
            #player_status{id = RoleId, guild = #status_guild{id = GuildId}} = Status,
            case GuildId > 0 of
                true -> mod_guild:send_red_point(RoleId, Type);
                false -> skip
            end;
        true -> skip
    end.

%% 发送错误码
send_error_code(Sid, ErrorCode) ->
    {ok, BinData} = pt_110:write(11000, [ErrorCode]),
    lib_server_send:send_to_sid(Sid, BinData).

%% 跨服聊天 - 跨服中心进程执行
do_send_clusters_msg(Range) ->
    case Range of
        {all, BinData} ->
            clusters_center:apply_to_all_node(lib_server_send, send_to_all, [BinData]);
        {scene, _SceneId, _BinData} -> %% 大场景
            clusters_center:apply_to_all_node(mod_chat_agent, send_msg, [[Range]]);
        {{channel, ?CHAT_CHANNEL_BEACH}, _ServerId, SceneId, ScenePoolId, BinData} ->
            lib_server_send:send_to_scene(SceneId, ScenePoolId, BinData);
        {{channel, ?CHAT_CHANNEL_WORLD_KF}, ServerId, BinData} ->
            mod_chat_zone:zone_chat(ServerId, BinData);
        {{channel, ?CHAT_CHANNEL_COUNTRY}, ServerId, BinData} ->
            mod_chat_zone:country_chat(ServerId, BinData);
        {{channel, ?CHAT_CHANNEL_GUARD_RECRUIT}, _ServerId, BinData} ->
            MinLv = lib_module:get_open_lv(?MOD_DUN_MULTI, 2),
            clusters_center:apply_to_all_node(mod_chat_agent, send_msg, [[{all_lv, {MinLv, 999}, BinData}]]);
        {SceneId, ScenePoolId, CopyId, BinData} -> %% 房间
            clusters_center:apply_to_all_node(lib_server_send, send_to_scene, [SceneId, ScenePoolId, CopyId, BinData]);
        {SceneId, ScenePoolId, BinData} -> %% 场景线
            clusters_center:apply_to_all_node(lib_server_send, send_to_scene, [SceneId, ScenePoolId, BinData]);
        _ ->
            skip
    end.

%%send_channel_world_kf(Channel, Id, ReceiveId, PackData, BinData) ->
%%    mod_chat_cache:save_cache(Channel, Id, ReceiveId, PackData),
%%    clusters_center:apply_to_all_node(lib_server_send, send_to_all, [all_lv, {?CHAT_LV_WORLD_KF, 65535}, BinData], 10).


%% ----------------------------------------------------------------
%% 系统动态消息
%% ----------------------------------------------------------------

%% 发送传闻、电视、服务端消息。(读取 data_language:get/2 配置)
%% @param BroadcastRange 广播方式：
%%          {scene, SceneId, ScenePoolId, CopyId}   场景ID、场景进程id、副本ID
%%          {scene, SceneId, ScenePoolId}           场景ID、场景进程id
%%          {guild, GuildId}                        公会 ID
%%          {all_guild}                             所有的公会
%%          {realm, Realm}                          阵营值(国家)
%%          {team, TeamId}                          队伍传闻
%%          {player, PlayerId}                      玩家自己
%%          {all}                                   世界传闻(默认传闻)
%% @param ModuleId（后台配置）      模块id          data_language:get/2 的第一个参数
%% @param Id（后台配置）            传闻id          data_language:get/2 的第二个参数
%% @param Msg 内容（后台配置）      [param1,param2,...]
send_TV(BroadcastRange, ModuleId, Id, Msg) ->
    BinData = make_tv(ModuleId, Id, Msg),
    case BroadcastRange of
        %% 场景类不能直接lib_server_send:send_to_scene/3,4，对跨服类场景会出现多服广播问题
        {scene, SceneId} ->
            mod_chat_agent:send_msg([{scene, SceneId, BinData}]);
        {scene, SceneId, ScenePoolId} ->
            mod_chat_agent:send_msg([{scene, SceneId, ScenePoolId, BinData}]);
        {scene, SceneId, ScenePoolId, CopyId} ->
            mod_chat_agent:send_msg([{scene, SceneId, ScenePoolId, CopyId, BinData}]);
        {guild, GuildId} -> lib_server_send:send_to_guild(GuildId, BinData);
        {all_guild} -> lib_server_send:send_to_all_guild(BinData);
        {realm, Realm} -> lib_server_send:send_to_realm(Realm, BinData);
        {team, TeamId} -> lib_server_send:send_to_team(TeamId, BinData);
        {player, PlayerId} -> lib_server_send:send_to_uid(PlayerId, BinData);
        {all} -> lib_server_send:send_to_all(BinData);
        {all_lv, {MinLv, MaxLv}} -> lib_server_send:send_to_all(all_lv, {MinLv, MaxLv}, BinData);
        {all_exclude, RoleIds} -> lib_server_send:send_to_all(all_exclude, RoleIds, BinData);
        {cluster} ->
            clusters_node:apply_cast(?MODULE, do_send_clusters_tv, [BinData]);
        {cluster, Target} ->
            clusters_node:apply_cast(?MODULE, do_send_clusters_tv, [Target, BinData]);
        {all_cluster} ->
            clusters_center:apply_to_all_node(lib_server_send, send_to_all, [BinData]);
        {zone, ZoneId} ->
            case clusters_api:is_center() of
                true  ->
                    czone:zone_apply_cast(ZoneId, {lib_server_send, send_to_all, [BinData]});
                false ->
                    clusters_node:apply_cast(czone, zone_apply_cast, [ZoneId, {lib_server_send, send_to_all, [BinData]}])
            end;
        {zone, ZoneId, Faction} ->
            case clusters_api:is_center() of
                true  ->
                    czone:zone_faction_apply_cast(ZoneId, Faction, {lib_server_send, send_to_all, [BinData]});
                false ->
                    clusters_node:apply_cast(czone, zone_faction_apply_cast, [ZoneId, Faction, {lib_server_send, send_to_all, [BinData]}])
            end;
        _ -> lib_server_send:send_to_all(BinData)
    end.

%% 制作传闻协议包
make_tv(ModuleId, Id, Msg) ->
    case data_language:get(ModuleId, Id) of
        #language{tip = Tip} -> skip;
        _ -> Tip = 0
    end,
    MsgList = util:link_list(Msg),
    {ok, BinData} = pt_110:write(11015, [ModuleId, Id, MsgList, Tip]),
    BinData.
do_send_clusters_tv(Servers, BinData) when is_list(Servers) ->
    [
        clusters_center:apply_cast(ServerId, lib_server_send, send_to_all, [BinData])
        || ServerId <- Servers
    ];
do_send_clusters_tv(Target, BinData) ->
    clusters_center:apply_cast(Target, lib_server_send, send_to_all, [BinData]).

do_send_clusters_tv(BinData) ->
    clusters_center:apply_to_all_node(lib_server_send, send_to_all, [BinData]).

%% 发送玩家传闻、电视、服务端消息。(读取 data_language 配置)
%% @param BroadcastRange 广播方式：
%%          {scene, SceneId, ScenePoolId, CopyId}   场景ID、场景进程id、副本ID
%%          {scene, SceneId, ScenePoolId}           场景ID、场景进程id
%%          {guild, GuildId}                        公会 ID
%%          {realm, Realm}                          阵营值(国家)
%%          {team, TeamId}                          队伍传闻
%%          {all}                                   世界传闻(默认传闻)
%% @param Figure #figure{} 需要显示的玩家figure
%% @param ModuleId（后台配置）      模块id
%% @param Id（后台配置）            传闻id
%% @param Msg 内容（后台配置）      [param1,param2,...]
send_TV(BroadcastRange, RoleId, Figure, ModuleId, Id, Msg) ->
    SerId = config:get_server_id(),
    BinData = make_tv(SerId, RoleId, Figure, ModuleId, Id, Msg),
    case BroadcastRange of
        {scene, SceneId, ScenePoolId, CopyId} -> lib_server_send:send_to_scene(SceneId, ScenePoolId, CopyId, BinData);
        {scene, SceneId, ScenePoolId} -> lib_server_send:send_to_scene(SceneId, ScenePoolId, BinData);
        {guild, GuildId} -> lib_server_send:send_to_guild(GuildId, BinData);
        {realm, Realm} -> lib_server_send:send_to_realm(Realm, BinData);
        {team, TeamId} -> lib_server_send:send_to_team(TeamId, BinData);
        {player, PlayerId} -> lib_server_send:send_to_uid(PlayerId, BinData);
        {all} -> lib_server_send:send_to_all(BinData);
        _ -> lib_server_send:send_to_all(BinData)
    end.

%% 制作传闻协议包
make_tv(SerId, RoleId, Figure, ModuleId, Id, Msg) ->
    case data_language:get(ModuleId, Id) of
        #language{tip = Tip} -> skip;
        _ -> Tip = 0
    end,
    MsgList = util:link_list(Msg),
    {ok, BinData} = pt_110:write(11018, [SerId, RoleId, Figure, ModuleId, Id, MsgList, Tip]),
    BinData.

%% 发送频道的错误码
%% send_channel_error_code() ->
%%     {ErrorCodeInt, ErrorCodeArgs} = util:parse_error_code(ErrorCode),
%%       {ok, BinData} = pt_110:write(11017, [?CHAT_WINDOW_ONLY, ErrorCodeInt, ErrorCodeArgs]),

%% ----------------------------------------------------------------
%% 聊天公用函数
%% ----------------------------------------------------------------

%% 记录1分钟内玩家聊天内容
%% @Content 聊天内容
%% @return [IsSame, NewPlayer]
%%  IsSame 0:不相同 1:相同
record_content(Player, Channel, Content) ->
    Now = utime:unixtime(),
    case Player#player_status.chat of
        #status_chat{prev_record_time = PreTimeL, record_content = RecordContent} = Chat ->
            case lists:keyfind(Channel, 1, PreTimeL) of
                {_Channel, PreTime} -> ok;
                _ -> PreTime = 0
            end,
            case (Now - PreTime) > 60 of
                true ->
                    TimeL = lists:keystore(Channel, 1, PreTimeL, {Channel, Now}),
                    NewChat = #status_chat{prev_record_time = TimeL, record_content = [{Channel, Content}]},
                    NewPlayer = Player#player_status{chat = NewChat},
                    [?CHAT_CONTENT_SAME_NO, NewPlayer];
                false ->
                    %% 限制记录数量为10条,控制占用内存量
                    NewRecordContent = lists:sublist([{Channel, Content} | RecordContent], 10),
                    NewChat = Chat#status_chat{record_content = NewRecordContent},
                    NewPlayer = Player#player_status{chat = NewChat},
                    %% 取得过去聊天相同信息的数量
                    Num = count_num(RecordContent, {Channel, Content}, 0),
                    %% 取得过去聊天最新的一条信息
                    FirstContent = lists:sublist(RecordContent, 1),
                    %% 跟上一条信息是否相同，过去聊天跟当前信息相同大于一条，返回的IsSmae=1
                    case lists:member({Channel, Content}, FirstContent) orelse Num >= 1 of
                        true -> [?CHAT_CONTENT_SAME_YES, NewPlayer];
                        false -> [?CHAT_CONTENT_SAME_NO, NewPlayer]
                    end
            end;
        _ ->
            NewChat = #status_chat{prev_record_time = [{Channel, Now}], record_content = [{Channel, Content}]},
            NewPlayer = Player#player_status{chat = NewChat},
            [?CHAT_CONTENT_SAME_NO, NewPlayer]
    end.

%% 统计聊天内容出现次数
count_num([], {_Channel, _Content}, Num) -> Num;
count_num([{Channel, Content} | T], {Channel, Content}, Num) -> count_num(T, {Channel, Content}, Num + 1);
count_num([_ | T], {Channel, Content}, Num) -> count_num(T, {Channel, Content}, Num).

%% ----------------------------------------------------------------
%% 禁言处理
%% ----------------------------------------------------------------

%% 系统公告
update_sys_notice_4_houtai() ->
    update_sys_notice(),
    ok.

%% 更新系统公告
update_sys_notice() ->
    Now = utime:unixtime(),
    % Q = io_lib:format(<<"select `open_day`,`merge_day`,`source`,`type`,`color`,`content`,`url`,`num`,`span`,`start_time`,`end_time`,`status` from notice where `end_time` > ~p and status = 1">>, [Now]),
    Q = io_lib:format(<<"select `open_day`,`merge_day`, `id`, `source`,`type`,`color`, `content`, `url`, `num`,`span`,`start_time`,`end_time`,`status` from notice where `end_time` > ~p">>, [Now]),
    Result = case db:get_all(Q) of
                 [] ->
                     ets:delete(?ETS_SYS_NOTICE, sys_notice),
                     [];
                 List ->
                     ets:insert(?ETS_SYS_NOTICE, {sys_notice, List}),
                     List
             end,
    NewResult = [list_to_tuple(T) || [OpenL, MergeL | T] <- Result, check_notice({util:bitstring_to_term(OpenL), util:bitstring_to_term(MergeL)}, [open_day, merge_day])],
    {ok, BinData} = pt_110:write(11050, [NewResult]),
    lib_server_send:send_to_all(BinData),
    ok.

chat_11050_help(Sid) ->
    Now = utime:unixtime(),
    case ets:lookup(ets_sys_notice, sys_notice) of
        [] ->
            % Q = io_lib:format(<<"select `open_day`,`merge_day`,`source`,`type`,`color`, `content`, `url`, `num`,`span`,`start_time`,`end_time`,`status` from notice where `end_time` > ~p and status = 1">>, [Now]),
            Q = io_lib:format(<<"select `open_day`,`merge_day`, `id`, `source`,`type`,`color`, `content`, `url`, `num`,`span`,`start_time`,`end_time`,`status` from notice where `end_time` > ~p">>, [Now]),
            case db:get_all(Q) of
                [] -> Result = [];
                List -> Result = List
            end,
            ets:insert(ets_sys_notice, {sys_notice, Result});
        [{sys_notice, List}] ->
            % Result = [ [OpenD, MergeD, Source, Type, Color, Content, Url, Num, Span, Start, End, Status] || [OpenD, MergeD, Source, Type, Color, Content, Url, Num, Span, Start, End, Status|_] <- List, End > Now, Status == 1]
            Result = [[OpenD, MergeD, Id, Source, Type, Color, Content, Url, Num, Span, Start, End, Status] || [OpenD, MergeD, Id, Source, Type, Color, Content, Url, Num, Span, Start, End, Status | _] <- List, End > Now]
    end,
    NewResult = [list_to_tuple(T) || [OpenL, MergeL | T] <- Result, check_notice({util:bitstring_to_term(OpenL), util:bitstring_to_term(MergeL)}, [open_day, merge_day])],
    {ok, BinData} = pt_110:write(11050, [NewResult]),
    lib_server_send:send_to_sid(Sid, BinData),
    ok.

check_notice(_Info, []) -> true;
check_notice(Info, [H | T]) ->
    case do_check_notice(Info, H) of
        true -> check_notice(Info, T);
        _ -> false
    end;
check_notice(_, _) -> false.

do_check_notice({OpenLim, _}, open_day) ->
    Now = util:get_open_day(),
    case OpenLim of
        [] -> true;
        OpenLim when is_list(OpenLim) ->
            CheckL = [1 || {Start, End} <- OpenLim, Now >= Start, Now =< End],
            CheckL =/= [];
        _ -> false
    end;
do_check_notice({_, MergeLim}, merge_day) ->
    Now = util:get_merge_day(),
    case MergeLim of
        [] -> true;
        MergeLim when is_list(MergeLim) ->
            CheckL = [1 || {Start, End} <- MergeLim, Now >= Start, Now =< End],
            CheckL =/= [];
        _ -> false
    end;
do_check_notice(_, _) -> false.


%% 禁言
%% @param RoleIdList [id1,id2,...]
forbid_chat_4_houtai(RoleIdList, LimitTime) ->
    forbid_chat(RoleIdList, LimitTime, ?TALK_LIMIT_TYPE_1).

%% 禁言
%% @param RoleIdList [id1,id2,...]
%% @param TalkLimitType ?TALK_LIMIT_TYPE_1,... 见chat.hrl
forbid_chat(RoleIdList, LimitTime, TalkLimitType) ->
    TalkLimTime = case LimitTime of
                      0 -> ?TALK_LIMIT_TIME_0;
                      1 -> ?TALK_LIMIT_TIME_1;
                      2 -> ?TALK_LIMIT_TIME_2;
                      3 -> ?TALK_LIMIT_TIME_3;
                      4 -> ?TALK_LIMIT_TIME_4;
                      5 -> ?TALK_LIMIT_TIME_5;
                      6 -> ?TALK_LIMIT_TIME_6;
                      7 -> ?TALK_LIMIT_TIME_7;
                      8 -> ?TALK_LIMIT_TIME_8;
                      9 -> ?TALK_LIMIT_TIME_9
                  end,
    Time = utime:unixtime() + TalkLimTime,
    lists:foreach(fun(RoleId) ->
        case mod_chat_agent:lookup(RoleId) of
            [] ->
                log_forbid_chat(RoleId, TalkLimitType, TalkLimTime),
                forbid_chat_db(RoleId, 1, TalkLimitType, Time);
            [_Player] ->
                log_forbid_chat(RoleId, TalkLimitType, TalkLimTime),
                lib_player:update_player_info(RoleId, [{talk_lim, 1}, {talk_lim_type, TalkLimitType}, {talk_lim_time, Time}]),
                forbid_chat_db(RoleId, 1, TalkLimitType, Time)
        end
                  end, RoleIdList),
    del_all_msg(RoleIdList),
    1.

%% 释放禁言
%% @param RoleIdList [id1,id2,...]
release_chat_4_houtai(RoleIdList) ->
    release_chat(RoleIdList, ?TALK_RELEASE_TYPE_1).

%% 释放禁言
%% @param RoleIdList [id1,id2,...]
%% @param Type 解禁类型 1 gm或管理员手动解禁 2自动解禁
release_chat(RoleIdList, Type) ->
    Time = utime:unixtime(),
    lists:foreach(fun(RoleId) ->
        %%检查目标玩家是否在线
        case mod_chat_agent:lookup(RoleId) of
            [] ->
                log_realese_chat(RoleId, Type),
                clear_talk_reported_count(RoleId),
                forbid_chat_db(RoleId, 0, 0, Time);
            [_Player] ->
                log_realese_chat(RoleId, Type),
                lib_player:update_player_info(RoleId, [{talk_lim, 0}, {talk_lim_time, Time}]),
                clear_talk_reported_count(RoleId),
                forbid_chat_db(RoleId, 0, 0, Time)
        end
                  end, RoleIdList),
    1.

%% 设置禁言
%% @param TalkLimit 0:没有禁言 1:禁言
%% @param TalkLimitType 1 gm或指导员禁言 2违反规则自动禁言A  3违反规则自动禁言B  4被举报次数过多禁言 5世界发言次数限制 6被霸主禁言跨服聊天
%% @param TalkLimitTime
forbid_chat_db(RoleId, TalkLimit, TalkLimitType, TalkLimitTime) ->
    db:execute(io_lib:format(?SQL_UPDATE_TALK_LIM, [TalkLimit, TalkLimitType, TalkLimitTime, RoleId])),
    ok.

%% 获取禁言信息
get_talk_lim(Id) ->
    db:get_row(io_lib:format(?SQL_SELECT_TALK_LIM, [Id])).

%% 被禁言通知
be_lim_talk(Status) ->
    Talk_lim_time = Status#player_status.talk_lim_time,
    Time = utime:unixtime(),
    Release_after = Talk_lim_time - Time, %%release_after秒后释放禁言
    {ok, BinData} = pt_110:write(11042, Release_after),
    lib_server_send:send_to_sid(Status#player_status.sid, BinData),
    ok.

%% 玩家是否被禁言
is_chat_forbid(Id) ->
    case mod_chat_agent:lookup(Id) of
        [] ->
            [Talk_lim, _, _] = get_talk_lim(Id),
            case Talk_lim of
                1 -> true;
                _ -> false
            end;
        [Player] ->
            case Player#ets_unite.talk_lim of
                1 -> true;
                _ -> false
            end
    end.

%% 清空聊天举报次数(每次解放禁言的时候重置)
clear_talk_reported_count(Id) ->
    SQL = io_lib:format("update `player_login` set `talk_reported_count` = 0 where `id` = ~p", [Id]),
    db:execute(SQL).

%% 禁言日志
log_forbid_chat(Id, Type, LimitTime) ->
    Info = data_chat_m:get_forbid_type(Type, [LimitTime div 60]),
    IsAdmin = data_chat_m:is_admin_forbid(Type),
    NowTime = utime:unixtime(),
    lib_log_api:log_ban(3, erlang:integer_to_list(Id), Info, NowTime, IsAdmin).

%% 禁言解除日志
log_realese_chat(Id, Type) ->
    Info = data_chat_m:get_release_type(Type),
    IsAdmin = data_chat_m:is_admin_forbid(Type),
    NowTime = utime:unixtime(),
    lib_log_api:log_ban(4, erlang:integer_to_list(Id), Info, NowTime, IsAdmin).

%% 发言过频通知
chat_too_frequently(_Id, Sid) ->
    {ok, BinData} = pt_110:write(11014, []),
    lib_server_send:send_to_sid(Sid, BinData).


%% 通知客户端清理玩家聊天数据
del_all_msg(RoleIdList) ->
    mod_chat_cache:delete_public_cache_with_uid(RoleIdList),
    {ok, BinData} = pt_110:write(11046, [RoleIdList]),
    lib_server_send:send_to_all(BinData),
    ok.

%% 直接往前端发信息
send_sys_msg(RoleId, Msg) when is_integer(RoleId)->
    {ok, BinData} = pt_110:write(11020, Msg),
    lib_server_send:send_to_uid(RoleId, BinData);
send_sys_msg(Sid, Msg) ->
    {ok, BinData} = pt_110:write(11020, Msg),
    lib_server_send:send_to_sid(Sid, BinData).

%% 直通发送错误码
send_sys_error_code(Sid, ErrCode, Arg) ->
    {ok, BinData} = pt_110:write(11017, [1, ErrCode, Arg]),
    lib_server_send:send_to_sid(Sid, BinData).

