const querystring = require('node:querystring');
const got = require('got');
const timeout = 15000;

const push_config = {
  HITOKOTO: true, // 启用一言（随机句子）

  BARK_PUSH: '', // bark IP 或设备码，例：https://api.day.app/DxHcxxxxxRxxxxxxcm/
  BARK_ARCHIVE: '', // bark 推送是否存档
  BARK_GROUP: '', // bark 推送分组
  BARK_SOUND: '', // bark 推送声音
  BARK_ICON: '', // bark 推送图标
  BARK_LEVEL: '', // bark 推送时效性
  BARK_URL: '', // bark 推送跳转URL

  DD_BOT_SECRET: '', // 钉钉机器人的 DD_BOT_SECRET
  DD_BOT_TOKEN: '', // 钉钉机器人的 DD_BOT_TOKEN

  FSKEY: '', // 飞书机器人的 FSKEY

  // 推送到个人QQ：http://127.0.0.1/send_private_msg
  // 群：http://127.0.0.1/send_group_msg
  GOBOT_URL: '', // go-cqhttp
  // 推送到个人QQ 填入 user_id=个人QQ
  // 群 填入 group_id=QQ群
  GOBOT_QQ: '', // go-cqhttp 的推送群或用户
  GOBOT_TOKEN: '', // go-cqhttp 的 access_token

  GOTIFY_URL: '', // gotify地址,如https://push.example.de:8080
  GOTIFY_TOKEN: '', // gotify的消息应用token
  GOTIFY_PRIORITY: 0, // 推送消息优先级,默认为0

  IGOT_PUSH_KEY: '', // iGot 聚合推送的 IGOT_PUSH_KEY，例如：https://push.hellyw.com/XXXXXXXX

  PUSH_KEY: '', // server 酱的 PUSH_KEY，兼容旧版与 Turbo 版

  DEER_KEY: '', // PushDeer 的 PUSHDEER_KEY
  DEER_URL: '', // PushDeer 的 PUSHDEER_URL

  CHAT_URL: '', // synology chat url
  CHAT_TOKEN: '', // synology chat token

  // 官方文档：https://www.pushplus.plus/
  PUSH_PLUS_TOKEN: '', // pushplus 推送的用户令牌
  PUSH_PLUS_USER: '', // pushplus 推送的群组编码
  PUSH_PLUS_TEMPLATE: 'html', // pushplus 发送模板，支持html,txt,json,markdown,cloudMonitor,jenkins,route,pay
  PUSH_PLUS_CHANNEL: 'wechat', // pushplus 发送渠道，支持wechat,webhook,cp,mail,sms
  PUSH_PLUS_WEBHOOK: '', // pushplus webhook编码，可在pushplus公众号上扩展配置出更多渠道
  PUSH_PLUS_CALLBACKURL: '', // pushplus 发送结果回调地址，会把推送最终结果通知到这个地址上
  PUSH_PLUS_TO: '', // pushplus 好友令牌，微信公众号渠道填写好友令牌，企业微信渠道填写企业微信用户id

  // 微加机器人，官方网站：https://www.weplusbot.com/
  WE_PLUS_BOT_TOKEN: '', // 微加机器人的用户令牌
  WE_PLUS_BOT_RECEIVER: '', // 微加机器人的消息接收人
  WE_PLUS_BOT_VERSION: 'pro', //微加机器人调用版本，pro和personal；为空默认使用pro(专业版)，个人版填写：personal

  QMSG_KEY: '', // qmsg 酱的 QMSG_KEY
  QMSG_TYPE: '', // qmsg 酱的 QMSG_TYPE

  QYWX_ORIGIN: 'https://qyapi.weixin.qq.com', // 企业微信代理地址

  /*
    此处填你企业微信应用消息的值(详见文档 https://work.weixin.qq.com/api/doc/90000/90135/90236)
    环境变量名 QYWX_AM依次填入 corpid,corpsecret,touser(注:多个成员ID使用|隔开),agentid,消息类型(选填,不填默认文本消息类型)
    注意用,号隔开(英文输入法的逗号)，例如：wwcff56746d9adwers,B-791548lnzXBE6_BWfxdf3kSTMJr9vFEPKAbh6WERQ,mingcheng,1000001,2COXgjH2UIfERF2zxrtUOKgQ9XklUqMdGSWLBoW_lSDAdafat
    可选推送消息类型(推荐使用图文消息（mpnews）):
    - 文本卡片消息: 0 (数字零)
    - 文本消息: 1 (数字一)
    - 图文消息（mpnews）: 素材库图片id, 可查看此教程(http://note.youdao.com/s/HMiudGkb)或者(https://note.youdao.com/ynoteshare1/index.html?id=1a0c8aff284ad28cbd011b29b3ad0191&type=note)
  */
  QYWX_AM: '', // 企业微信应用

  QYWX_KEY: '', // 企业微信机器人的 webhook(详见文档 https://work.weixin.qq.com/api/doc/90000/90136/91770)，例如：693a91f6-7xxx-4bc4-97a0-0ec2sifa5aaa

  TG_BOT_TOKEN: '', // tg 机器人的 TG_BOT_TOKEN，例：1407203283:AAG9rt-6RDaaX0HBLZQq0laNOh898iFYaRQ
  TG_USER_ID: '', // tg 机器人的 TG_USER_ID，例：1434078534
  TG_API_HOST: 'https://api.telegram.org', // tg 代理 api
  TG_PROXY_AUTH: '', // tg 代理认证参数
  TG_PROXY_HOST: '', // tg 机器人的 TG_PROXY_HOST
  TG_PROXY_PORT: '', // tg 机器人的 TG_PROXY_PORT

  AIBOTK_KEY: '', // 智能微秘书 个人中心的apikey 文档地址：http://wechat.aibotk.com/docs/about
  AIBOTK_TYPE: '', // 智能微秘书 发送目标 room 或 contact
  AIBOTK_NAME: '', // 智能微秘书  发送群名 或者好友昵称和type要对应好

  SMTP_SERVICE: '', // 邮箱服务名称，比如 126、163、Gmail、QQ 等，支持列表 https://github.com/nodemailer/nodemailer/blob/master/lib/well-known/services.json
  SMTP_EMAIL: '', // SMTP 收发件邮箱，通知将会由自己发给自己
  SMTP_PASSWORD: '', // SMTP 登录密码，也可能为特殊口令，视具体邮件服务商说明而定
  SMTP_NAME: '', // SMTP 收发件人姓名，可随意填写

  PUSHME_KEY: '', // 官方文档：https://push.i-i.me，PushMe 酱的 PUSHME_KEY

  // CHRONOCAT API https://chronocat.vercel.app/install/docker/official/
  CHRONOCAT_QQ: '', // 个人: user_id=个人QQ 群则填入 group_id=QQ群 多个用英文;隔开同时支持个人和群
  CHRONOCAT_TOKEN: '', // 填写在CHRONOCAT文件生成的访问密钥
  CHRONOCAT_URL: '', // Red 协议连接地址 例： http://127.0.0.1:16530

  WEBHOOK_URL: '', // 自定义通知 请求地址
  WEBHOOK_BODY: '', // 自定义通知 请求体
  WEBHOOK_HEADERS: '', // 自定义通知 请求头
  WEBHOOK_METHOD: '', // 自定义通知 请求方法
  WEBHOOK_CONTENT_TYPE: '', // 自定义通知 content-type

  NTFY_URL: '', // ntfy地址,如https://ntfy.sh,默认为https://ntfy.sh
  NTFY_TOPIC: '', // ntfy的消息应用topic
  NTFY_PRIORITY: '3', // 推送消息优先级,默认为3

  // 官方文档: https://wxpusher.zjiecode.com/docs/
  // 管理后台: https://wxpusher.zjiecode.com/admin/
  WXPUSHER_APP_TOKEN: '', // wxpusher 的 appToken
  WXPUSHER_TOPIC_IDS: '', // wxpusher 的 主题ID，多个用英文分号;分隔 topic_ids 与 uids 至少配置一个才行
  WXPUSHER_UIDS: '', // wxpusher 的 用户ID，多个用英文分号;分隔 topic_ids 与 uids 至少配置一个才行
};

for (const key in push_config) {
  const v = process.env[key];
  if (v) {
    push_config[key] = v;
  }
}

const $ = {
  post: (params, callback) => {
    const { url, ...others } = params;
    got.post(url, others).then(
      (res) => {
        let body = res.body;
        try {
          body = JSON.parse(body);
        } catch (error) {}
        callback(null, res, body);
      },
      (err) => {
        callback(err?.response?.body || err);
      },
    );
  },
  get: (params, callback) => {
    const { url, ...others } = params;
    got.get(url, others).then(
      (res) => {
        let body = res.body;
        try {
          body = JSON.parse(body);
        } catch (error) {}
        callback(null, res, body);
      },
      (err) => {
        callback(err?.response?.body || err);
      },
    );
  },
  logErr: console.log,
};

async function one() {
  const url = 'https://v1.hitokoto.cn/';
  const res = await got.get(url);
  const body = JSON.parse(res.body);
  return `${body.hitokoto}    ----${body.from}`;
}

function gotifyNotify(text, desp) {
  return new Promise((resolve) => {
    const { GOTIFY_URL, GOTIFY_TOKEN, GOTIFY_PRIORITY } = push_config;
    if (GOTIFY_URL && GOTIFY_TOKEN) {
      const options = {
        url: `${GOTIFY_URL}/message?token=${GOTIFY_TOKEN}`,
        body: `title=${encodeURIComponent(text)}&message=${encodeURIComponent(
          desp,
        )}&priority=${GOTIFY_PRIORITY}`,
        headers: {
          'Content-Type': 'application/x-www-form-urlencoded',
        },
      };
      $.post(options, (err, resp, data) => {
        try {
          if (err) {
            console.log('Gotify 发送通知调用API失败😞\n', err);
          } else {
            if (data.id) {
              console.log('Gotify 发送通知消息成功🎉\n');
            } else {
              console.log(`Gotify 发送通知调用API失败😞 ${data.message}\n`);
            }
          }
        } catch (e) {
          $.logErr(e, resp);
        } finally {
          resolve();
        }
      });
    } else {
      resolve();
    }
  });
}

function gobotNotify(text, desp) {
  return new Promise((resolve) => {
    const { GOBOT_URL, GOBOT_TOKEN, GOBOT_QQ } = push_config;
    if (GOBOT_URL) {
      const options = {
        url: `${GOBOT_URL}?access_token=${GOBOT_TOKEN}&${GOBOT_QQ}`,
        json: { message: `${text}\n${desp}` },
        headers: {
          'Content-Type': 'application/json',
        },
        timeout,
      };
      $.post(options, (err, resp, data) => {
        try {
          if (err) {
            console.log('Go-cqhttp 通知调用API失败😞\n', err);
          } else {
            if (data.retcode === 0) {
              console.log('Go-cqhttp 发送通知消息成功🎉\n');
            } else if (data.retcode === 100) {
              console.log(`Go-cqhttp 发送通知消息异常 ${data.errmsg}\n`);
            } else {
              console.log(`Go-cqhttp 发送通知消息异常 ${JSON.stringify(data)}`);
            }
          }
        } catch (e) {
          $.logErr(e, resp);
        } finally {
          resolve(data);
        }
      });
    } else {
      resolve();
    }
  });
}

function serverNotify(text, desp) {
  return new Promise((resolve) => {
    const { PUSH_KEY } = push_config;
    if (PUSH_KEY) {
      // 微信server酱推送通知一个\n不会换行，需要两个\n才能换行，故做此替换
      desp = desp.replace(/[\n\r]/g, '\n\n');

      const matchResult = PUSH_KEY.match(/^sctp(\d+)t/i);
      const options = {
        url:
          matchResult && matchResult[1]
            ? `https://${matchResult[1]}.push.ft07.com/send/${PUSH_KEY}.send`
            : `https://sctapi.ftqq.com/${PUSH_KEY}.send`,
        body: `text=${encodeURIComponent(text)}&desp=${encodeURIComponent(
          desp,
        )}`,
        headers: {
          'Content-Type': 'application/x-www-form-urlencoded',
        },
        timeout,
      };
      $.post(options, (err, resp, data) => {
        try {
          if (err) {
            console.log('Server 酱发送通知调用API失败😞\n', err);
          } else {
            // server酱和Server酱·Turbo版的返回json格式不太一样
            if (data.errno === 0 || data.data.errno === 0) {
              console.log('Server 酱发送通知消息成功🎉\n');
            } else if (data.errno === 1024) {
              // 一分钟内发送相同的内容会触发
              console.log(`Server 酱发送通知消息异常 ${data.errmsg}\n`);
            } else {
              console.log(`Server 酱发送通知消息异常 ${JSON.stringify(data)}`);
            }
          }
        } catch (e) {
          $.logErr(e, resp);
        } finally {
          resolve(data);
        }
      });
    } else {
      resolve();
    }
  });
}

function pushDeerNotify(text, desp) {
  return new Promise((resolve) => {
    const { DEER_KEY, DEER_URL } = push_config;
    if (DEER_KEY) {
      // PushDeer 建议对消息内容进行 urlencode
      desp = encodeURI(desp);
      const options = {
        url: DEER_URL || `https://api2.pushdeer.com/message/push`,
        body: `pushkey=${DEER_KEY}&text=${text}&desp=${desp}&type=markdown`,
        headers: {
          'Content-Type': 'application/x-www-form-urlencoded',
        },
        timeout,
      };
      $.post(options, (err, resp, data) => {
        try {
          if (err) {
            console.log('PushDeer 通知调用API失败😞\n', err);
          } else {
            // 通过返回的result的长度来判断是否成功
            if (
              data.content.result.length !== undefined &&
              data.content.result.length > 0
            ) {
              console.log('PushDeer 发送通知消息成功🎉\n');
            } else {
              console.log(
                `PushDeer 发送通知消息异常😞 ${JSON.stringify(data)}`,
              );
            }
          }
        } catch (e) {
          $.logErr(e, resp);
        } finally {
          resolve(data);
        }
      });
    } else {
      resolve();
    }
  });
}

function chatNotify(text, desp) {
  return new Promise((resolve) => {
    const { CHAT_URL, CHAT_TOKEN } = push_config;
    if (CHAT_URL && CHAT_TOKEN) {
      // 对消息内容进行 urlencode
      desp = encodeURI(desp);
      const options = {
        url: `${CHAT_URL}${CHAT_TOKEN}`,
        body: `payload={"text":"${text}\n${desp}"}`,
        headers: {
          'Content-Type': 'application/x-www-form-urlencoded',
        },
      };
      $.post(options, (err, resp, data) => {
        try {
          if (err) {
            console.log('Chat 发送通知调用API失败😞\n', err);
          } else {
            if (data.success) {
              console.log('Chat 发送通知消息成功🎉\n');
            } else {
              console.log(`Chat 发送通知消息异常 ${JSON.stringify(data)}`);
            }
          }
        } catch (e) {
          $.logErr(e);
        } finally {
          resolve(data);
        }
      });
    } else {
      resolve();
    }
  });
}

function barkNotify(text, desp, params = {}) {
  return new Promise((resolve) => {
    let {
      BARK_PUSH,
      BARK_ICON,
      BARK_SOUND,
      BARK_GROUP,
      BARK_LEVEL,
      BARK_ARCHIVE,
      BARK_URL,
    } = push_config;
    if (BARK_PUSH) {
      // 兼容BARK本地用户只填写设备码的情况
      if (!BARK_PUSH.startsWith('http')) {
        BARK_PUSH = `https://api.day.app/${BARK_PUSH}`;
      }
      const options = {
        url: `${BARK_PUSH}`,
        json: {
          title: text,
          body: desp,
          icon: BARK_ICON,
          sound: BARK_SOUND,
          group: BARK_GROUP,
          isArchive: BARK_ARCHIVE,
          level: BARK_LEVEL,
          url: BARK_URL,
          ...params,
        },
        headers: {
          'Content-Type': 'application/json',
        },
        timeout,
      };
      $.post(options, (err, resp, data) => {
        try {
          if (err) {
            console.log('Bark APP 发送通知调用API失败😞\n', err);
          } else {
            if (data.code === 200) {
              console.log('Bark APP 发送通知消息成功🎉\n');
            } else {
              console.log(`Bark APP 发送通知消息异常 ${data.message}\n`);
            }
          }
        } catch (e) {
          $.logErr(e, resp);
        } finally {
          resolve();
        }
      });
    } else {
      resolve();
    }
  });
}

function tgBotNotify(text, desp) {
  return new Promise((resolve) => {
    const {
      TG_BOT_TOKEN,
      TG_USER_ID,
      TG_PROXY_HOST,
      TG_PROXY_PORT,
      TG_API_HOST,
      TG_PROXY_AUTH,
    } = push_config;
    if (TG_BOT_TOKEN && TG_USER_ID) {
      let options = {
        url: `${TG_API_HOST}/bot${TG_BOT_TOKEN}/sendMessage`,
        json: {
          chat_id: `${TG_USER_ID}`,
          text: `${text}\n\n${desp}`,
          disable_web_page_preview: true,
        },
        headers: {
          'Content-Type': 'application/json',
        },
        timeout,
      };
      if (TG_PROXY_HOST && TG_PROXY_PORT) {
        const { HttpProxyAgent, HttpsProxyAgent } = require('hpagent');
        const _options = {
          keepAlive: true,
          keepAliveMsecs: 1000,
          maxSockets: 256,
          maxFreeSockets: 256,
          proxy: `http://${TG_PROXY_AUTH}${TG_PROXY_HOST}:${TG_PROXY_PORT}`,
        };
        const httpAgent = new HttpProxyAgent(_options);
        const httpsAgent = new HttpsProxyAgent(_options);
        const agent = {
          http: httpAgent,
          https: httpsAgent,
        };
        options.agent = agent;
      }
      $.post(options, (err, resp, data) => {
        try {
          if (err) {
            console.log('Telegram 发送通知消息失败😞\n', err);
          } else {
            if (data.ok) {
              console.log('Telegram 发送通知消息成功🎉。\n');
            } else if (data.error_code === 400) {
              console.log(
                '请主动给bot发送一条消息并检查接收用户ID是否正确。\n',
              );
            } else if (data.error_code === 401) {
              console.log('Telegram bot token 填写错误。\n');
            }
          }
        } catch (e) {
          $.logErr(e, resp);
        } finally {
          resolve(data);
        }
      });
    } else {
      resolve();
    }
  });
}
function ddBotNotify(text, desp) {
  return new Promise((resolve) => {
    const { DD_BOT_TOKEN, DD_BOT_SECRET } = push_config;
    const options = {
      url: `https://oapi.dingtalk.com/robot/send?access_token=${DD_BOT_TOKEN}`,
      json: {
        msgtype: 'text',
        text: {
          content: `${text}\n\n${desp}`,
        },
      },
      headers: {
        'Content-Type': 'application/json',
      },
      timeout,
    };
    if (DD_BOT_TOKEN && DD_BOT_SECRET) {
      const crypto = require('crypto');
      const dateNow = Date.now();
      const hmac = crypto.createHmac('sha256', DD_BOT_SECRET);
      hmac.update(`${dateNow}\n${DD_BOT_SECRET}`);
      const result = encodeURIComponent(hmac.digest('base64'));
      options.url = `${options.url}&timestamp=${dateNow}&sign=${result}`;
      $.post(options, (err, resp, data) => {
        try {
          if (err) {
            console.log('钉钉发送通知消息失败😞\n', err);
          } else {
            if (data.errcode === 0) {
              console.log('钉钉发送通知消息成功🎉\n');
            } else {
              console.log(`钉钉发送通知消息异常 ${data.errmsg}\n`);
            }
          }
        } catch (e) {
          $.logErr(e, resp);
        } finally {
          resolve(data);
        }
      });
    } else if (DD_BOT_TOKEN) {
      $.post(options, (err, resp, data) => {
        try {
          if (err) {
            console.log('钉钉发送通知消息失败😞\n', err);
          } else {
            if (data.errcode === 0) {
              console.log('钉钉发送通知消息成功🎉\n');
            } else {
              console.log(`钉钉发送通知消息异常 ${data.errmsg}\n`);
            }
          }
        } catch (e) {
          $.logErr(e, resp);
        } finally {
          resolve(data);
        }
      });
    } else {
      resolve();
    }
  });
}

function qywxBotNotify(text, desp) {
  return new Promise((resolve) => {
    const { QYWX_ORIGIN, QYWX_KEY } = push_config;
    const options = {
      url: `${QYWX_ORIGIN}/cgi-bin/webhook/send?key=${QYWX_KEY}`,
      json: {
        msgtype: 'text',
        text: {
          content: `${text}\n\n${desp}`,
        },
      },
      headers: {
        'Content-Type': 'application/json',
      },
      timeout,
    };
    if (QYWX_KEY) {
      $.post(options, (err, resp, data) => {
        try {
          if (err) {
            console.log('企业微信发送通知消息失败😞\n', err);
          } else {
            if (data.errcode === 0) {
              console.log('企业微信发送通知消息成功🎉。\n');
            } else {
              console.log(`企业微信发送通知消息异常 ${data.errmsg}\n`);
            }
          }
        } catch (e) {
          $.logErr(e, resp);
        } finally {
          resolve(data);
        }
      });
    } else {
      resolve();
    }
  });
}

function ChangeUserId(desp) {
  const { QYWX_AM } = push_config;
  const QYWX_AM_AY = QYWX_AM.split(',');
  if (QYWX_AM_AY[2]) {
    const userIdTmp = QYWX_AM_AY[2].split('|');
    let userId = '';
    for (let i = 0; i < userIdTmp.length; i++) {
      const count = '账号' + (i + 1);
      const count2 = '签到号 ' + (i + 1);
      if (desp.match(count2)) {
        userId = userIdTmp[i];
      }
    }
    if (!userId) userId = QYWX_AM_AY[2];
    return userId;
  } else {
    return '@all';
  }
}

async function qywxamNotify(text, desp) {
  const MAX_LENGTH = 900;
  if (desp.length > MAX_LENGTH) {
    let d = desp.substr(0, MAX_LENGTH) + '\n==More==';
    await do_qywxamNotify(text, d);
    await qywxamNotify(text, desp.substr(MAX_LENGTH));
  } else {
    return await do_qywxamNotify(text, desp);
  }
}

function do_qywxamNotify(text, desp) {
  return new Promise((resolve) => {
    const { QYWX_AM, QYWX_ORIGIN } = push_config;
    if (QYWX_AM) {
      const QYWX_AM_AY = QYWX_AM.split(',');
      const options_accesstoken = {
        url: `${QYWX_ORIGIN}/cgi-bin/gettoken`,
        json: {
          corpid: `${QYWX_AM_AY[0]}`,
          corpsecret: `${QYWX_AM_AY[1]}`,
        },
        headers: {
          'Content-Type': 'application/json',
        },
        timeout,
      };
      $.post(options_accesstoken, (err, resp, json) => {
        let html = desp.replace(/\n/g, '<br/>');
        let accesstoken = json.access_token;
        let options;

        switch (QYWX_AM_AY[4]) {
          case '0':
            options = {
              msgtype: 'textcard',
              textcard: {
                title: `${text}`,
                description: `${desp}`,
                url: 'https://github.com/whyour/qinglong',
                btntxt: '更多',
              },
            };
            break;

          case '1':
            options = {
              msgtype: 'text',
              text: {
                content: `${text}\n\n${desp}`,
              },
            };
            break;

          default:
            options = {
              msgtype: 'mpnews',
              mpnews: {
                articles: [
                  {
                    title: `${text}`,
                    thumb_media_id: `${QYWX_AM_AY[4]}`,
                    author: `智能助手`,
                    content_source_url: ``,
                    content: `${html}`,
                    digest: `${desp}`,
                  },
                ],
              },
            };
        }
        if (!QYWX_AM_AY[4]) {
          // 如不提供第四个参数,则默认进行文本消息类型推送
          options = {
            msgtype: 'text',
            text: {
              content: `${text}\n\n${desp}`,
            },
          };
        }
        options = {
          url: `${QYWX_ORIGIN}/cgi-bin/message/send?access_token=${accesstoken}`,
          json: {
            touser: `${ChangeUserId(desp)}`,
            agentid: `${QYWX_AM_AY[3]}`,
            safe: '0',
            ...options,
          },
          headers: {
            'Content-Type': 'application/json',
          },
        };

        $.post(options, (err, resp, data) => {
          try {
            if (err) {
              console.log(
                '成员ID:' +
                  ChangeUserId(desp) +
                  '企业微信应用消息发送通知消息失败😞\n',
                err,
              );
            } else {
              if (data.errcode === 0) {
                console.log(
                  '成员ID:' +
                    ChangeUserId(desp) +
                    '企业微信应用消息发送通知消息成功🎉。\n',
                );
              } else {
                console.log(
                  `企业微信应用消息发送通知消息异常 ${data.errmsg}\n`,
                );
              }
            }
          } catch (e) {
            $.logErr(e, resp);
          } finally {
            resolve(data);
          }
        });
      });
    } else {
      resolve();
    }
  });
}

function iGotNotify(text, desp, params = {}) {
  return new Promise((resolve) => {
    const { IGOT_PUSH_KEY } = push_config;
    if (IGOT_PUSH_KEY) {
      // 校验传入的IGOT_PUSH_KEY是否有效
      const IGOT_PUSH_KEY_REGX = new RegExp('^[a-zA-Z0-9]{24}$');
      if (!IGOT_PUSH_KEY_REGX.test(IGOT_PUSH_KEY)) {
        console.log('您所提供的 IGOT_PUSH_KEY 无效\n');
        resolve();
        return;
      }
      const options = {
        url: `https://push.hellyw.com/${IGOT_PUSH_KEY.toLowerCase()}`,
        body: `title=${text}&content=${desp}&${querystring.stringify(params)}`,
        headers: {
          'Content-Type': 'application/x-www-form-urlencoded',
        },
        timeout,
      };
      $.post(options, (err, resp, data) => {
        try {
          if (err) {
            console.log('IGot 发送通知调用API失败😞\n', err);
          } else {
            if (data.ret === 0) {
              console.log('IGot 发送通知消息成功🎉\n');
            } else {
              console.log(`IGot 发送通知消息异常 ${data.errMsg}\n`);
            }
          }
        } catch (e) {
          $.logErr(e, resp);
        } finally {
          resolve(data);
        }
      });
    } else {
      resolve();
    }
  });
}

function pushPlusNotify(text, desp) {
  return new Promise((resolve) => {
    const {
      PUSH_PLUS_TOKEN,
      PUSH_PLUS_USER,
      PUSH_PLUS_TEMPLATE,
      PUSH_PLUS_CHANNEL,
      PUSH_PLUS_WEBHOOK,
      PUSH_PLUS_CALLBACKURL,
      PUSH_PLUS_TO,
    } = push_config;
    if (PUSH_PLUS_TOKEN) {
      desp = desp.replace(/[\n\r]/g, '<br>'); // 默认为html, 不支持plaintext
      const body = {
        token: `${PUSH_PLUS_TOKEN}`,
        title: `${text}`,
        content: `${desp}`,
        topic: `${PUSH_PLUS_USER}`,
        template: `${PUSH_PLUS_TEMPLATE}`,
        channel: `${PUSH_PLUS_CHANNEL}`,
        webhook: `${PUSH_PLUS_WEBHOOK}`,
        callbackUrl: `${PUSH_PLUS_CALLBACKURL}`,
        to: `${PUSH_PLUS_TO}`,
      };
      const options = {
        url: `https://www.pushplus.plus/send`,
        body: JSON.stringify(body),
        headers: {
          'Content-Type': ' application/json',
        },
        timeout,
      };
      $.post(options, (err, resp, data) => {
        try {
          if (err) {
            console.log(
              `pushplus 发送${
                PUSH_PLUS_USER ? '一对多' : '一对一'
              }通知消息失败😞\n`,
              err,
            );
          } else {
            if (data.code === 200) {
              console.log(
                `pushplus 发送${
                  PUSH_PLUS_USER ? '一对多' : '一对一'
                }通知请求成功🎉，可根据流水号查询推送结果：${
                  data.data
                }\n注意：请求成功并不代表推送成功，如未收到消息，请到pushplus官网使用流水号查询推送最终结果`,
              );
            } else {
              console.log(
                `pushplus 发送${
                  PUSH_PLUS_USER ? '一对多' : '一对一'
                }通知消息异常 ${data.msg}\n`,
              );
            }
          }
        } catch (e) {
          $.logErr(e, resp);
        } finally {
          resolve(data);
        }
      });
    } else {
      resolve();
    }
  });
}

function wePlusBotNotify(text, desp) {
  return new Promise((resolve) => {
    const { WE_PLUS_BOT_TOKEN, WE_PLUS_BOT_RECEIVER, WE_PLUS_BOT_VERSION } =
      push_config;
    if (WE_PLUS_BOT_TOKEN) {
      let template = 'txt';
      if (desp.length > 800) {
        desp = desp.replace(/[\n\r]/g, '<br>');
        template = 'html';
      }
      const body = {
        token: `${WE_PLUS_BOT_TOKEN}`,
        title: `${text}`,
        content: `${desp}`,
        template: `${template}`,
        receiver: `${WE_PLUS_BOT_RECEIVER}`,
        version: `${WE_PLUS_BOT_VERSION}`,
      };
      const options = {
        url: `https://www.weplusbot.com/send`,
        body: JSON.stringify(body),
        headers: {
          'Content-Type': ' application/json',
        },
        timeout,
      };
      $.post(options, (err, resp, data) => {
        try {
          if (err) {
            console.log(`微加机器人发送通知消息失败😞\n`, err);
          } else {
            if (data.code === 200) {
              console.log(`微加机器人发送通知消息完成🎉\n`);
            } else {
              console.log(`微加机器人发送通知消息异常 ${data.msg}\n`);
            }
          }
        } catch (e) {
          $.logErr(e, resp);
        } finally {
          resolve(data);
        }
      });
    } else {
      resolve();
    }
  });
}

function aibotkNotify(text, desp) {
  return new Promise((resolve) => {
    const { AIBOTK_KEY, AIBOTK_TYPE, AIBOTK_NAME } = push_config;
    if (AIBOTK_KEY && AIBOTK_TYPE && AIBOTK_NAME) {
      let json = {};
      let url = '';
      switch (AIBOTK_TYPE) {
        case 'room':
          url = 'https://api-bot.aibotk.com/openapi/v1/chat/room';
          json = {
            apiKey: `${AIBOTK_KEY}`,
            roomName: `${AIBOTK_NAME}`,
            message: {
              type: 1,
              content: `【青龙快讯】\n\n${text}\n${desp}`,
            },
          };
          break;
        case 'contact':
          url = 'https://api-bot.aibotk.com/openapi/v1/chat/contact';
          json = {
            apiKey: `${AIBOTK_KEY}`,
            name: `${AIBOTK_NAME}`,
            message: {
              type: 1,
              content: `【青龙快讯】\n\n${text}\n${desp}`,
            },
          };
          break;
      }
      const options = {
        url: url,
        json,
        headers: {
          'Content-Type': 'application/json',
        },
        timeout,
      };
      $.post(options, (err, resp, data) => {
        try {
          if (err) {
            console.log('智能微秘书发送通知消息失败😞\n', err);
          } else {
            if (data.code === 0) {
              console.log('智能微秘书发送通知消息成功🎉。\n');
            } else {
              console.log(`智能微秘书发送通知消息异常 ${data.error}\n`);
            }
          }
        } catch (e) {
          $.logErr(e, resp);
        } finally {
          resolve(data);
        }
      });
    } else {
      resolve();
    }
  });
}

function fsBotNotify(text, desp) {
  return new Promise((resolve) => {
    const { FSKEY } = push_config;
    if (FSKEY) {
      const options = {
        url: `https://open.feishu.cn/open-apis/bot/v2/hook/${FSKEY}`,
        json: { msg_type: 'text', content: { text: `${text}\n\n${desp}` } },
        headers: {
          'Content-Type': 'application/json',
        },
        timeout,
      };
      $.post(options, (err, resp, data) => {
        try {
          if (err) {
            console.log('飞书发送通知调用API失败😞\n', err);
          } else {
            if (data.StatusCode === 0 || data.code === 0) {
              console.log('飞书发送通知消息成功🎉\n');
            } else {
              console.log(`飞书发送通知消息异常 ${data.msg}\n`);
            }
          }
        } catch (e) {
          $.logErr(e, resp);
        } finally {
          resolve(data);
        }
      });
    } else {
      resolve();
    }
  });
}

async function smtpNotify(text, desp) {
  const { SMTP_EMAIL, SMTP_PASSWORD, SMTP_SERVICE, SMTP_NAME } = push_config;
  if (![SMTP_EMAIL, SMTP_PASSWORD].every(Boolean) || !SMTP_SERVICE) {
    return;
  }

  try {
    const nodemailer = require('nodemailer');
    const transporter = nodemailer.createTransport({
      service: SMTP_SERVICE,
      auth: {
        user: SMTP_EMAIL,
        pass: SMTP_PASSWORD,
      },
    });

    const addr = SMTP_NAME ? `"${SMTP_NAME}" <${SMTP_EMAIL}>` : SMTP_EMAIL;
    const info = await transporter.sendMail({
      from: addr,
      to: addr,
      subject: text,
      html: `${desp.replace(/\n/g, '<br/>')}`,
    });

    transporter.close();

    if (info.messageId) {
      console.log('SMTP 发送通知消息成功🎉\n');
      return true;
    }
    console.log('SMTP 发送通知消息失败😞\n');
  } catch (e) {
    console.log('SMTP 发送通知消息出现异常😞\n', e);
  }
}

function pushMeNotify(text, desp, params = {}) {
  return new Promise((resolve) => {
    const { PUSHME_KEY, PUSHME_URL } = push_config;
    if (PUSHME_KEY) {
      const options = {
        url: PUSHME_URL || 'https://push.i-i.me',
        json: { push_key: PUSHME_KEY, title: text, content: desp, ...params },
        headers: {
          'Content-Type': 'application/json',
        },
        timeout,
      };
      $.post(options, (err, resp, data) => {
        try {
          if (err) {
            console.log('PushMe 发送通知调用API失败😞\n', err);
          } else {
            if (data === 'success') {
              console.log('PushMe 发送通知消息成功🎉\n');
            } else {
              console.log(`PushMe 发送通知消息异常 ${data}\n`);
            }
          }
        } catch (e) {
          $.logErr(e, resp);
        } finally {
          resolve(data);
        }
      });
    } else {
      resolve();
    }
  });
}

function chronocatNotify(title, desp) {
  return new Promise((resolve) => {
    const { CHRONOCAT_TOKEN, CHRONOCAT_QQ, CHRONOCAT_URL } = push_config;
    if (!CHRONOCAT_TOKEN || !CHRONOCAT_QQ || !CHRONOCAT_URL) {
      resolve();
      return;
    }

    const user_ids = CHRONOCAT_QQ.match(/user_id=(\d+)/g)?.map(
      (match) => match.split('=')[1],
    );
    const group_ids = CHRONOCAT_QQ.match(/group_id=(\d+)/g)?.map(
      (match) => match.split('=')[1],
    );

    const url = `${CHRONOCAT_URL}/api/message/send`;
    const headers = {
      'Content-Type': 'application/json',
      Authorization: `Bearer ${CHRONOCAT_TOKEN}`,
    };

    for (const [chat_type, ids] of [
      [1, user_ids],
      [2, group_ids],
    ]) {
      if (!ids) {
        continue;
      }
      for (const chat_id of ids) {
        const data = {
          peer: {
            chatType: chat_type,
            peerUin: chat_id,
          },
          elements: [
            {
              elementType: 1,
              textElement: {
                content: `${title}\n\n${desp}`,
              },
            },
          ],
        };
        const options = {
          url: url,
          json: data,
          headers,
          timeout,
        };
        $.post(options, (err, resp, data) => {
          try {
            if (err) {
              console.log('Chronocat 发送QQ通知消息失败😞\n', err);
            } else {
              if (chat_type === 1) {
                console.log(`Chronocat 个人消息 ${ids}推送成功🎉`);
              } else {
                console.log(`Chronocat 群消息 ${ids}推送成功🎉`);
              }
            }
          } catch (e) {
            $.logErr(e, resp);
          } finally {
            resolve(data);
          }
        });
      }
    }
  });
}

function qmsgNotify(text, desp) {
  return new Promise((resolve) => {
    const { QMSG_KEY, QMSG_TYPE } = push_config;
    if (QMSG_KEY && QMSG_TYPE) {
      const options = {
        url: `https://qmsg.zendee.cn/${QMSG_TYPE}/${QMSG_KEY}`,
        body: `msg=${text}\n\n${desp.replace('----', '-')}`,
        headers: {
          'Content-Type': 'application/x-www-form-urlencoded',
        },
        timeout,
      };
      $.post(options, (err, resp, data) => {
        try {
          if (err) {
            console.log('Qmsg 发送通知调用API失败😞\n', err);
          } else {
            if (data.code === 0) {
              console.log('Qmsg 发送通知消息成功🎉\n');
            } else {
              console.log(`Qmsg 发送通知消息异常 ${data}\n`);
            }
          }
        } catch (e) {
          $.logErr(e, resp);
        } finally {
          resolve(data);
        }
      });
    } else {
      resolve();
    }
  });
}

function webhookNotify(text, desp) {
  return new Promise((resolve) => {
    const {
      WEBHOOK_URL,
      WEBHOOK_BODY,
      WEBHOOK_HEADERS,
      WEBHOOK_CONTENT_TYPE,
      WEBHOOK_METHOD,
    } = push_config;
    if (
      !WEBHOOK_METHOD ||
      !WEBHOOK_URL ||
      (!WEBHOOK_URL.includes('$title') && !WEBHOOK_BODY.includes('$title'))
    ) {
      resolve();
      return;
    }

    const headers = parseHeaders(WEBHOOK_HEADERS);
    const body = parseBody(WEBHOOK_BODY, WEBHOOK_CONTENT_TYPE, (v) =>
      v
        ?.replaceAll('$title', text?.replaceAll('\n', '\\n'))
        ?.replaceAll('$content', desp?.replaceAll('\n', '\\n')),
    );
    const bodyParam = formatBodyFun(WEBHOOK_CONTENT_TYPE, body);
    const options = {
      method: WEBHOOK_METHOD,
      headers,
      allowGetBody: true,
      ...bodyParam,
      timeout,
      retry: 1,
    };

    const formatUrl = WEBHOOK_URL.replaceAll(
      '$title',
      encodeURIComponent(text),
    ).replaceAll('$content', encodeURIComponent(desp));
    got(formatUrl, options).then((resp) => {
      try {
        if (resp.statusCode !== 200) {
          console.log(`自定义发送通知消息失败😞 ${resp.body}\n`);
        } else {
          console.log(`自定义发送通知消息成功🎉 ${resp.body}\n`);
        }
      } catch (e) {
        $.logErr(e, resp);
      } finally {
        resolve(resp.body);
      }
    });
  });
}

function ntfyNotify(text, desp) {
  function encodeRFC2047(text) {
    const encodedBase64 = Buffer.from(text).toString('base64');
    return `=?utf-8?B?${encodedBase64}?=`;
  }

  return new Promise((resolve) => {
    const { NTFY_URL, NTFY_TOPIC, NTFY_PRIORITY } = push_config;
    if (NTFY_TOPIC) {
      const options = {
        url: `${NTFY_URL || 'https://ntfy.sh'}/${NTFY_TOPIC}`,
        body: `${desp}`,
        headers: {
          Title: `${encodeRFC2047(text)}`,
          Priority: NTFY_PRIORITY || '3',
        },
        timeout,
      };
      $.post(options, (err, resp, data) => {
        try {
          if (err) {
            console.log('Ntfy 通知调用API失败😞\n', err);
          } else {
            if (data.id) {
              console.log('Ntfy 发送通知消息成功🎉\n');
            } else {
              console.log(`Ntfy 发送通知消息异常 ${JSON.stringify(data)}`);
            }
          }
        } catch (e) {
          $.logErr(e, resp);
        } finally {
          resolve(data);
        }
      });
    } else {
      resolve();
    }
  });
}

function wxPusherNotify(text, desp) {
  return new Promise((resolve) => {
    const { WXPUSHER_APP_TOKEN, WXPUSHER_TOPIC_IDS, WXPUSHER_UIDS } =
      push_config;
    if (WXPUSHER_APP_TOKEN) {
      // 处理topic_ids，将分号分隔的字符串转为数组
      const topicIds = WXPUSHER_TOPIC_IDS
        ? WXPUSHER_TOPIC_IDS.split(';')
            .map((id) => id.trim())
            .filter((id) => id)
            .map((id) => parseInt(id))
        : [];

      // 处理uids，将分号分隔的字符串转为数组
      const uids = WXPUSHER_UIDS
        ? WXPUSHER_UIDS.split(';')
            .map((uid) => uid.trim())
            .filter((uid) => uid)
        : [];

      // topic_ids uids 至少有一个
      if (!topicIds.length && !uids.length) {
        console.log(
          'wxpusher 服务的 WXPUSHER_TOPIC_IDS 和 WXPUSHER_UIDS 至少设置一个!!',
        );
        return resolve();
      }

      const body = {
        appToken: WXPUSHER_APP_TOKEN,
        content: `<h1>${text}</h1><br/><div style='white-space: pre-wrap;'>${desp}</div>`,
        summary: text,
        contentType: 2,
        topicIds: topicIds,
        uids: uids,
        verifyPayType: 0,
      };

      const options = {
        url: 'https://wxpusher.zjiecode.com/api/send/message',
        body: JSON.stringify(body),
        headers: {
          'Content-Type': 'application/json',
        },
        timeout,
      };

      $.post(options, (err, resp, data) => {
        try {
          if (err) {
            console.log('wxpusher发送通知消息失败！\n', err);
          } else {
            if (data.code === 1000) {
              console.log('wxpusher发送通知消息完成！');
            } else {
              console.log(`wxpusher发送通知消息异常：${data.msg}`);
            }
          }
        } catch (e) {
          $.logErr(e, resp);
        } finally {
          resolve(data);
        }
      });
    } else {
      resolve();
    }
  });
}

function parseString(input, valueFormatFn) {
  const regex = /(\w+):\s*((?:(?!\n\w+:).)*)/g;
  const matches = {};

  let match;
  while ((match = regex.exec(input)) !== null) {
    const [, key, value] = match;
    const _key = key.trim();
    if (!_key || matches[_key]) {
      continue;
    }

    let _value = value.trim();

    try {
      _value = valueFormatFn ? valueFormatFn(_value) : _value;
      const jsonValue = JSON.parse(_value);
      matches[_key] = jsonValue;
    } catch (error) {
      matches[_key] = _value;
    }
  }

  return matches;
}

function parseHeaders(headers) {
  if (!headers) return {};

  const parsed = {};
  let key;
  let val;
  let i;

  headers &&
    headers.split('\n').forEach(function parser(line) {
      i = line.indexOf(':');
      key = line.substring(0, i).trim().toLowerCase();
      val = line.substring(i + 1).trim();

      if (!key) {
        return;
      }

      parsed[key] = parsed[key] ? parsed[key] + ', ' + val : val;
    });

  return parsed;
}

function parseBody(body, contentType, valueFormatFn) {
  if (contentType === 'text/plain' || !body) {
    return valueFormatFn && body ? valueFormatFn(body) : body;
  }

  const parsed = parseString(body, valueFormatFn);

  switch (contentType) {
    case 'multipart/form-data':
      return Object.keys(parsed).reduce((p, c) => {
        p.append(c, parsed[c]);
        return p;
      }, new FormData());
    case 'application/x-www-form-urlencoded':
      return Object.keys(parsed).reduce((p, c) => {
        return p ? `${p}&${c}=${parsed[c]}` : `${c}=${parsed[c]}`;
      });
  }

  return parsed;
}

function formatBodyFun(contentType, body) {
  if (!body) return {};
  switch (contentType) {
    case 'application/json':
      return { json: body };
    case 'multipart/form-data':
      return { form: body };
    case 'application/x-www-form-urlencoded':
    case 'text/plain':
      return { body };
  }
  return {};
}

/**
 * sendNotify 推送通知功能
 * @param text 通知头
 * @param desp 通知体
 * @param params 某些推送通知方式点击弹窗可跳转, 例：{ url: 'https://abc.com' }
 * @returns {Promise<unknown>}
 */
async function sendNotify(text, desp, params = {}) {
  // 根据标题跳过一些消息推送，环境变量：SKIP_PUSH_TITLE 用回车分隔
  let skipTitle = process.env.SKIP_PUSH_TITLE;
  if (skipTitle) {
    if (skipTitle.split('\n').includes(text)) {
      console.info(text + '在 SKIP_PUSH_TITLE 环境变量内，跳过推送');
      return;
    }
  }

  if (push_config.HITOKOTO !== 'false') {
    desp += '\n\n' + (await one());
  }

  await Promise.all([
    serverNotify(text, desp), // 微信server酱
    pushPlusNotify(text, desp), // pushplus
    wePlusBotNotify(text, desp), // 微加机器人
    barkNotify(text, desp, params), // iOS Bark APP
    tgBotNotify(text, desp), // telegram 机器人
    ddBotNotify(text, desp), // 钉钉机器人
    qywxBotNotify(text, desp), // 企业微信机器人
    qywxamNotify(text, desp), // 企业微信应用消息推送
    iGotNotify(text, desp, params), // iGot
    gobotNotify(text, desp), // go-cqhttp
    gotifyNotify(text, desp), // gotify
    chatNotify(text, desp), // synolog chat
    pushDeerNotify(text, desp), // PushDeer
    aibotkNotify(text, desp), // 智能微秘书
    fsBotNotify(text, desp), // 飞书机器人
    smtpNotify(text, desp), // SMTP 邮件
    pushMeNotify(text, desp, params), // PushMe
    chronocatNotify(text, desp), // Chronocat
    webhookNotify(text, desp), // 自定义通知
    qmsgNotify(text, desp), // 自定义通知
    ntfyNotify(text, desp), // Ntfy
    wxPusherNotify(text, desp), // wxpusher
  ]);
}

module.exports = {
  sendNotify,
};
