可口可乐青龙签到脚本

2026-04-29 08:332阅读0评论SEO资讯
  • 内容介绍
  • 文章标签
  • 相关推荐
问题描述:

/**

  • 可口可乐吧 签到 - 青龙多账号版

  • 环境变量:

  • WXID: 备注#wxid,多账号用 & 或 换行 分隔

  • 示例: 账号1#wxid_xxxxxxxx&账号2#wxid_yyyyyyyy

  • 功能:

    1. 微信 code → Bearer token(带缓存)
    1. 查询签到状态
    1. 执行每日签到
    1. 查询积分
  • cron: 30 8 * * *

  • const: disabled = false
    */

const axios = require(‘axios’);
const fs = require(‘fs’);
const path = require(‘path’);

/* ==================== 常量配置 ==================== */
const APPID = ‘wxa5811e0426a94686’;
const BASE_URL = ‘https://member-api.icoke.cn’;
const UA = ‘Mozilla/5.0 (iPhone; CPU iPhone OS 16_3_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 MicroMessenger/8.0.59(0x18003b2e) NetType/4G Language/zh_CN’;
const REFERER = https://servicewechat.com/${APPID}/499/page-frame.html;

const SCRIPT_DIR = path.dirname(__filename);
const CACHE_FILE = path.join(SCRIPT_DIR, ‘可口可乐签到_cache.txt’);

/* ==================== 环境变量 ==================== */
const WECHAT_SERVER = process.env.WECHAT_SERVER;
const WXID_ENV = process.env.WXID;

if (!WECHAT_SERVER || !WXID_ENV) {
console.log(‘ 请设置 WECHAT_SERVER 和 WXID 环境变量’);
process.exit(1);
}

const wxidList = WXID_ENV.split(/[\n&]/).map(i => i.trim()).filter(i => i && i.includes(‘#’));
if (!wxidList.length) {
console.log(‘ WXID 格式错误,请使用 备注#wxid 格式’);
process.exit(1);
}

/* ==================== 缓存管理 ==================== */
function loadTokenCache() {
const cacheMap = {};
try {
if (fs.existsSync(CACHE_FILE)) {
fs.readFileSync(CACHE_FILE, ‘utf8’).split(/\n/).filter(Boolean).forEach(line => {
const idx = line.indexOf(‘#’);
if (idx > 0) {
const remark = line.substring(0, idx);
const token = line.substring(idx + 1);
cacheMap[remark] = token;
}
});
}
} catch (e) {}
return cacheMap;
}

function saveTokenCache(cacheMap) {
try {
const content = Object.entries(cacheMap).map(([r, t]) => ${r}#${t}).join(‘\n’);
fs.writeFileSync(CACHE_FILE, content || ‘#cache’, { encoding: ‘utf8’, flag: ‘w’ });
} catch (e) {}
}

/* ==================== 微信 code 换 token ==================== */
async function getCode(wxid) {
try {
const url = ${WECHAT_SERVER}/api/v1/wx/app/get/code;
const res = await axios.post(url, { wxid, appid: APPID }, { timeout: 10000 });
const code = res.data?.Data?.code;
if (!code) {
console.log(‘ getCode 响应:’, JSON.stringify(res.data));
return null;
}
return code;
} catch (e) {
console.log(‘ getCode 异常:’, e.message);
return null;
}
}

async function codeToToken(code) {
try {
const url = ${BASE_URL}/api/sp-portal/store/icoke/wechat/loginNoCache/${code};
const headers = {
‘Host’: ‘member-api.icoke.cn’,
‘content-type’: ‘application/json’,
‘Accept’: ‘application/json, text/plain, /’,
‘Accept-Encoding’: ‘gzip,compress,br,deflate’,
‘User-Agent’: UA,
‘Referer’: REFERER,
};
const res = await axios.get(url, { headers, timeout: 10000 });
const body = res.data;
// 实际抓包确认:token 在响应体的 jwtString 字段
if (body && body.jwtString) {
return body.jwtString;
}
console.log(‘ codeToToken 响应:’, JSON.stringify(body).substring(0, 300));
return null;
} catch (e) {
console.log(‘ codeToToken 异常:’, e.message);
return null;
}
}

async function getValidToken(remark, wxid, force = false) {
const m = loadTokenCache();
if (force) delete m[remark];
if (m[remark]) {
console.log(‘ 使用缓存 token’);
return m[remark];
}
console.log(‘ 获取微信 code…’);
const code = await getCode(wxid);
if (!code) return null;
console.log(✅ code: ${code.substring(0, 10)}...);
const token = await codeToToken(code);
if (!token) return null;
m[remark] = token;
saveTokenCache(m);
console.log(‘ token 已缓存’);
return token;
}

/* ==================== 判断 token 失效 ==================== */
function isTokenInvalid(data) {
if (!data) return false;
const msg = (data.message || data.msg || ‘’).toLowerCase();
return msg.includes(‘未登录’) || msg.includes(‘token’) || msg.includes(‘登录’) ||
data.code === ‘401’ || data.code === 401 ||
data.status === 401 || data.httpStatus === 401;
}

/* ==================== 公共请求头 ==================== */
function getHeaders(token) {
return {
‘Host’: ‘member-api.icoke.cn’,
‘Authorization’: token,
‘content-type’: ‘application/json’,
‘Accept’: ‘application/json, text/plain, /’,
‘Accept-Encoding’: ‘gzip,compress,br,deflate’,
‘User-Agent’: UA,
‘Referer’: REFERER,
};
}

/* ==================== API 请求封装 ==================== */
async function apiGet(path, token, params) {
try {
const res = await axios.get(${BASE_URL}${path}, {
headers: getHeaders(token),
params,
timeout: 15000,
});
return res.data;
} catch (e) {
console.log(❌ GET ${path} 异常:, e.message);
return null;
}
}

async function apiPost(path, token, data) {
try {
const res = await axios.post(${BASE_URL}${path}, data, {
headers: getHeaders(token),
timeout: 15000,
});
return res.data;
} catch (e) {
console.log(❌ POST ${path} 异常:, e.message);
return null;
}
}

/* ==================== 业务接口 ==================== */

async function getSignOutline(token) {
const res = await apiGet(‘/api/icoke-sign/icoke/mini/sign/main/getSignOutline’, token);
if (!res) return null;
if (isTokenInvalid(res)) return { needRefresh: true };
const today = new Date();
const y = today.getFullYear(), m = today.getMonth() + 1, d = today.getDate();
const list = res.data || ;
const todayRecord = list.find(r => r.year === y && r.month === m && r.day === d);
return {
signed: !!(todayRecord && todayRecord.exist),
totalDays: list.filter(r => r.exist).length,
point: todayRecord?.point || 0,
data: list,
raw: res,
};
}

async function doSign(token) {
const res = await apiGet(‘/api/icoke-sign/icoke/mini/sign/main/sign’, token);
if (!res) return { success: false, msg: ‘请求失败’ };
if (isTokenInvalid(res)) return { needRefresh: true };
if (res.success === true || res.isSuccess === true) {
return {
success: true,
point: res.point || 0,
msg: res.message || ‘签到成功’,
};
}

const msg = res.message || res.msg || JSON.stringify(res);
if (msg.includes(‘已签’) || msg.includes(‘重复’) || msg.includes(‘already’)) {
return { success: false, alreadySigned: true, msg };
}
return { success: false, msg };
}

/** 查询积分 */
async function getPoints(token) {
const res = await apiGet(‘/api/icoke-customer/icoke/mini/customer/main/points’, token);
if (!res) return null;
return {
point: res.point || 0,
experiencePoints: res.experiencePoints || 0,
frozenPoint: res.frozenPoint || 0,
};
}

/** 查询用户基本信息 */
async function getUserInfo(token) {
const res = await apiGet(‘/api/icoke-customer/icoke/mini/customer/main/base/info’, token);
if (!res) return null;
return {
name: res.name || ‘未知’,
mobile: res.mobile || ‘’,
grade: res.grade || ‘’,
};
}

/* ==================== 工具函数 ==================== */
function sleep(ms) {
return new Promise(r => setTimeout(r, ms));
}

/* ==================== 单账号执行 ==================== */
async function runSingleAccount(accountStr, index) {
const sepIdx = accountStr.indexOf(‘#’);
if (sepIdx <= 0) {
console.log(❌ 第${index}个账号格式错误,应为 备注#wxid);
return;
}
const remark = accountStr.substring(0, sepIdx);
const wxid = accountStr.substring(sepIdx + 1).trim();

console.log(\n====================================);
console.log(🚀 【可口可乐】${remark}(第${index}个));
console.log(====================================);

// 获取 token
let token = await getValidToken(remark, wxid);
if (!token) {
console.log(‘ 获取 token 失败,跳过’);
return;
}

// 查询签到状态
let outline = await getSignOutline(token);
if (outline?.needRefresh) {
console.log(‘ token 失效,重新获取…’);
token = await getValidToken(remark, wxid, true);
if (!token) { console.log(‘ 重新获取 token 失败’); return; }
outline = await getSignOutline(token);
}

if (outline) {
if (outline.signed) {
console.log(📅 签到状态:✅ 今日已签到 | 本月已签 ${outline.totalDays} 天);
} else {
console.log(📅 签到状态:❌ 今日未签到 | 本月已签 ${outline.totalDays} 天);
}
}

// 执行签到
let signResult = await doSign(token);
if (signResult?.needRefresh) {
console.log(‘ token 失效,重新获取…’);
token = await getValidToken(remark, wxid, true);
if (!token) { console.log(‘ 重新获取 token 失败’); return; }
signResult = await doSign(token);
}

if (signResult?.success) {
console.log(✅ 签到成功!获得积分:${signResult.point});
} else if (signResult?.alreadySigned) {
console.log(ℹ️ 今日已签到:${signResult.msg});
} else {
console.log(⚠️ 签到结果:${signResult?.msg || '未知'});
}

await sleep(1000);

// 查询积分
const pts = await getPoints(token);
if (pts) {
console.log(💰 当前积分:${pts.point} | 经验值:${pts.experiencePoints} | 冻结:${pts.frozenPoint});
}

// 查询用户信息
const userInfo = await getUserInfo(token);
if (userInfo) {
console.log(👤 会员:${userInfo.name} | 手机:${userInfo.mobile});
}

console.log(✨ ${remark} 执行完成\n);
}

/* ==================== 主函数 ==================== */
!(async () => {
console.log(📢 可口可乐吧签到 - 共检测到 ${wxidList.length} 个账号);
for (let i = 0; i < wxidList.length; i++) {
await runSingleAccount(wxidList[i], i + 1);
if (i < wxidList.length - 1) await sleep(2000);
}
console.log(‘ 全部账号执行完毕!’);
})();

网友解答:
--【壹】--:

这个积分能用来做什么呢,是不是换礼品还是可以换可乐喝


--【贰】--:

WECHAT_SERVER 这个要配什么?


--【叁】--:

佬友有 wechatserver渠道吗,这个有的话可以薅的羊毛就太多了


--【肆】--:

这个帖子有点随性了佬,建议来规范下帖子格式,加个代码引用啥的


--【伍】--:

放在代码里输出吧,另外,这个是啥?可口可乐还有什么活动?


--【陆】--:

我看这也不是二选一呀, 在 wxid 获取 code 时候还是要 wechat server 不然这个脚本没有作用


--【柒】--:

可以考虑弄个文档也可以吧?这样看有点别扭啊


--【捌】--:

感谢佬友的分享,佬友这个积分可以拿来干嘛


--【玖】--:

大佬可以试试用markdown的格式输出,目前这个看起来有点乱。最后感谢分享


--【拾】--:

感谢佬分享 用markdown的代码编辑 可读性比较高


--【拾壹】--: 小旭:

/**

  • 可口可乐吧 签到 - 青龙多账号版
  • 环境变量:
  • WXID: 备注#wxid,多账号用 & 或 换行 分隔
  • 示例: 账号1#wxid_xxxxxxxx&账号2#wxid_yyyyyyyy
  • 功能:
    1. 微信 code → Bearer token(带缓存)
    1. 查询签到状态
    1. 执行每日签到
    1. 查询积分
  • cron: 30 8 * * *
  • const: disabled = false
    */

const axios = require(‘axios’);
const fs = require(‘fs’);
const path = require(‘path’);

/* ==================== 常量配置 ==================== */
const APPID = ‘wxa5811e0426a94686’;
const BASE_URL = ‘https://member-api.icoke.cn’;
const UA = ‘Mozilla/5.0 (iPhone; CPU iPhone OS 16_3_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 MicroMessenger/8.0.59(0x18003b2e) NetType/4G Language/zh_CN’;
const REFERER = https://servicewechat.com/${APPID}/499/page-frame.html;

const SCRIPT_DIR = path.dirname(__filename);
const CACHE_FILE = path.join(SCRIPT_DIR, ‘可口可乐签到_cache.txt’);

/* ==================== 环境变量 ==================== */
const WECHAT_SERVER = process.env.WECHAT_SERVER;
const WXID_ENV = process.env.WXID;

if (!WECHAT_SERVER || !WXID_ENV) {
console.log(‘ 请设置 WECHAT_SERVER 和 WXID 环境变量’);
process.exit(1);
}

const wxidList = WXID_ENV.split(/[\n&]/).map(i => i.trim()).filter(i => i && i.includes(‘#’));
if (!wxidList.length) {
console.log(‘ WXID 格式错误,请使用 备注#wxid 格式’);
process.exit(1);
}

/* ==================== 缓存管理 ==================== */
function loadTokenCache() {
const cacheMap = {};
try {
if (fs.existsSync(CACHE_FILE)) {
fs.readFileSync(CACHE_FILE, ‘utf8’).split(/\n/).filter(Boolean).forEach(line => {
const idx = line.indexOf(‘#’);
if (idx > 0) {
const remark = line.substring(0, idx);
const token = line.substring(idx + 1);
cacheMap[remark] = token;
}
});
}
} catch (e) {}
return cacheMap;
}

function saveTokenCache(cacheMap) {
try {
const content = Object.entries(cacheMap).map(([r, t]) => ${r}#${t}).join(‘\n’);
fs.writeFileSync(CACHE_FILE, content || ‘#cache’, { encoding: ‘utf8’, flag: ‘w’ });
} catch (e) {}
}

/* ==================== 微信 code 换 token ==================== */
async function getCode(wxid) {
try {
const url = ${WECHAT_SERVER}/api/v1/wx/app/get/code;
const res = await axios.post(url, { wxid, appid: APPID }, { timeout: 10000 });
const code = res.data?.Data?.code;
if (!code) {
console.log(‘ getCode 响应:’, JSON.stringify(res.data));
return null;
}
return code;
} catch (e) {
console.log(‘ getCode 异常:’, e.message);
return null;
}
}

async function codeToToken(code) {
try {
const url = ${BASE_URL}/api/sp-portal/store/icoke/wechat/loginNoCache/${code};
const headers = {
‘Host’: ‘member-api.icoke.cn’,
‘content-type’: ‘application/json’,
‘Accept’: ‘application/json, text/plain, /’,
‘Accept-Encoding’: ‘gzip,compress,br,deflate’,
‘User-Agent’: UA,
‘Referer’: REFERER,
};
const res = await axios.get(url, { headers, timeout: 10000 });
const body = res.data;
// 实际抓包确认:token 在响应体的 jwtString 字段
if (body && body.jwtString) {
return body.jwtString;
}
console.log(‘ codeToToken 响应:’, JSON.stringify(body).substring(0, 300));
return null;
} catch (e) {
console.log(‘ codeToToken 异常:’, e.message);
return null;
}
}

async function getValidToken(remark, wxid, force = false) {
const m = loadTokenCache();
if (force) delete m[remark];
if (m[remark]) {
console.log(‘ 使用缓存 token’);
return m[remark];
}
console.log(‘ 获取微信 code…’);
const code = await getCode(wxid);
if (!code) return null;
console.log(✅ code: ${code.substring(0, 10)}...);
const token = await codeToToken(code);
if (!token) return null;
m[remark] = token;
saveTokenCache(m);
console.log(‘ token 已缓存’);
return token;
}

/* ==================== 判断 token 失效 ==================== */
function isTokenInvalid(data) {
if (!data) return false;
const msg = (data.message || data.msg || ‘’).toLowerCase();
return msg.includes(‘未登录’) || msg.includes(‘token’) || msg.includes(‘登录’) ||
data.code === ‘401’ || data.code === 401 ||
data.status === 401 || data.httpStatus === 401;
}

/* ==================== 公共请求头 ==================== */
function getHeaders(token) {
return {
‘Host’: ‘member-api.icoke.cn’,
‘Authorization’: token,
‘content-type’: ‘application/json’,
‘Accept’: ‘application/json, text/plain, /’,
‘Accept-Encoding’: ‘gzip,compress,br,deflate’,
‘User-Agent’: UA,
‘Referer’: REFERER,
};
}

/* ==================== API 请求封装 ==================== */
async function apiGet(path, token, params) {
try {
const res = await axios.get(${BASE_URL}${path}, {
headers: getHeaders(token),
params,
timeout: 15000,
});
return res.data;
} catch (e) {
console.log(❌ GET ${path} 异常:, e.message);
return null;
}
}

async function apiPost(path, token, data) {
try {
const res = await axios.post(${BASE_URL}${path}, data, {
headers: getHeaders(token),
timeout: 15000,
});
return res.data;
} catch (e) {
console.log(❌ POST ${path} 异常:, e.message);
return null;
}
}

/* ==================== 业务接口 ==================== */

async function getSignOutline(token) {
const res = await apiGet(‘/api/icoke-sign/icoke/mini/sign/main/getSignOutline’, token);
if (!res) return null;
if (isTokenInvalid(res)) return { needRefresh: true };
const today = new Date();
const y = today.getFullYear(), m = today.getMonth() + 1, d = today.getDate();
const list = res.data || ;
const todayRecord = list.find(r => r.year === y && r.month === m && r.day === d);
return {
signed: !!(todayRecord && todayRecord.exist),
totalDays: list.filter(r => r.exist).length,
point: todayRecord?.point || 0,
data: list,
raw: res,
};
}

async function doSign(token) {
const res = await apiGet(‘/api/icoke-sign/icoke/mini/sign/main/sign’, token);
if (!res) return { success: false, msg: ‘请求失败’ };
if (isTokenInvalid(res)) return { needRefresh: true };
if (res.success === true || res.isSuccess === true) {
return {
success: true,
point: res.point || 0,
msg: res.message || ‘签到成功’,
};
}

const msg = res.message || res.msg || JSON.stringify(res);
if (msg.includes(‘已签’) || msg.includes(‘重复’) || msg.includes(‘already’)) {
return { success: false, alreadySigned: true, msg };
}
return { success: false, msg };
}

/** 查询积分 */
async function getPoints(token) {
const res = await apiGet(‘/api/icoke-customer/icoke/mini/customer/main/points’, token);
if (!res) return null;
return {
point: res.point || 0,
experiencePoints: res.experiencePoints || 0,
frozenPoint: res.frozenPoint || 0,
};
}

/** 查询用户基本信息 */
async function getUserInfo(token) {
const res = await apiGet(‘/api/icoke-customer/icoke/mini/customer/main/base/info’, token);
if (!res) return null;
return {
name: res.name || ‘未知’,
mobile: res.mobile || ‘’,
grade: res.grade || ‘’,
};
}

/* ==================== 工具函数 ==================== */
function sleep(ms) {
return new Promise(r => setTimeout(r, ms));
}

/* ==================== 单账号执行 ==================== */
async function runSingleAccount(accountStr, index) {
const sepIdx = accountStr.indexOf(‘#’);
if (sepIdx <= 0) {
console.log(❌ 第${index}个账号格式错误,应为 备注#wxid);
return;
}
const remark = accountStr.substring(0, sepIdx);
const wxid = accountStr.substring(sepIdx + 1).trim();

console.log(\n====================================);
console.log(🚀 【可口可乐】${remark}(第${index}个));
console.log(====================================);

// 获取 token
let token = await getValidToken(remark, wxid);
if (!token) {
console.log(‘ 获取 token 失败,跳过’);
return;
}

// 查询签到状态
let outline = await getSignOutline(token);
if (outline?.needRefresh) {
console.log(‘ token 失效,重新获取…’);
token = await getValidToken(remark, wxid, true);
if (!token) { console.log(‘ 重新获取 token 失败’); return; }
outline = await getSignOutline(token);
}

if (outline) {
if (outline.signed) {
console.log(📅 签到状态:✅ 今日已签到 | 本月已签 ${outline.totalDays} 天);
} else {
console.log(📅 签到状态:❌ 今日未签到 | 本月已签 ${outline.totalDays} 天);
}
}

// 执行签到
let signResult = await doSign(token);
if (signResult?.needRefresh) {
console.log(‘ token 失效,重新获取…’);
token = await getValidToken(remark, wxid, true);
if (!token) { console.log(‘ 重新获取 token 失败’); return; }
signResult = await doSign(token);
}

if (signResult?.success) {
console.log(✅ 签到成功!获得积分:${signResult.point});
} else if (signResult?.alreadySigned) {
console.log(ℹ️ 今日已签到:${signResult.msg});
} else {
console.log(⚠️ 签到结果:${signResult?.msg || '未知'});
}

await sleep(1000);

// 查询积分
const pts = await getPoints(token);
if (pts) {
console.log(💰 当前积分:${pts.point} | 经验值:${pts.experiencePoints} | 冻结:${pts.frozenPoint});
}

// 查询用户信息
const userInfo = await getUserInfo(token);
if (userInfo) {
console.log(👤 会员:${userInfo.name} | 手机:${userInfo.mobile});
}

console.log(✨ ${remark} 执行完成\n);
}

/* ==================== 主函数 ==================== */
!(async () => {
console.log(📢 可口可乐吧签到 - 共检测到 ${wxidList.length} 个账号);
for (let i = 0; i < wxidList.length; i++) {
await runSingleAccount(wxidList[i], i + 1);
if (i < wxidList.length - 1) await sleep(2000);
}
console.log(‘ 全部账号执行完毕!’);
})();

是微信协议,群里别的佬发的,好像他自己搭建了一个微信协议


--【拾贰】--:

看了一下能兑换的东西不多,用处不大,花精力写这个有点费力不讨好


--【拾叁】--:

没有取code服务是无法运行这个脚本的,wxid好友就能拿到,必须取token才能签到,wxid不能签到啊


--【拾肆】--:

这个签到的积分能干些啥呢 还多个微信弄呀


--【拾伍】--:

这是有什么羊毛活动吗,可口可乐签到,佬能细说下


--【拾陆】--: 小旭:

WECHAT_SERVER

蹲一下,我也好奇,这个有点逆天了,好像是把微信小程序内部接口给逆向出来了吧


--【拾柒】--:

不用管wechatserver,去抓包wxid就行了,二选一

标签:软件开发
问题描述:

/**

  • 可口可乐吧 签到 - 青龙多账号版

  • 环境变量:

  • WXID: 备注#wxid,多账号用 & 或 换行 分隔

  • 示例: 账号1#wxid_xxxxxxxx&账号2#wxid_yyyyyyyy

  • 功能:

    1. 微信 code → Bearer token(带缓存)
    1. 查询签到状态
    1. 执行每日签到
    1. 查询积分
  • cron: 30 8 * * *

  • const: disabled = false
    */

const axios = require(‘axios’);
const fs = require(‘fs’);
const path = require(‘path’);

/* ==================== 常量配置 ==================== */
const APPID = ‘wxa5811e0426a94686’;
const BASE_URL = ‘https://member-api.icoke.cn’;
const UA = ‘Mozilla/5.0 (iPhone; CPU iPhone OS 16_3_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 MicroMessenger/8.0.59(0x18003b2e) NetType/4G Language/zh_CN’;
const REFERER = https://servicewechat.com/${APPID}/499/page-frame.html;

const SCRIPT_DIR = path.dirname(__filename);
const CACHE_FILE = path.join(SCRIPT_DIR, ‘可口可乐签到_cache.txt’);

/* ==================== 环境变量 ==================== */
const WECHAT_SERVER = process.env.WECHAT_SERVER;
const WXID_ENV = process.env.WXID;

if (!WECHAT_SERVER || !WXID_ENV) {
console.log(‘ 请设置 WECHAT_SERVER 和 WXID 环境变量’);
process.exit(1);
}

const wxidList = WXID_ENV.split(/[\n&]/).map(i => i.trim()).filter(i => i && i.includes(‘#’));
if (!wxidList.length) {
console.log(‘ WXID 格式错误,请使用 备注#wxid 格式’);
process.exit(1);
}

/* ==================== 缓存管理 ==================== */
function loadTokenCache() {
const cacheMap = {};
try {
if (fs.existsSync(CACHE_FILE)) {
fs.readFileSync(CACHE_FILE, ‘utf8’).split(/\n/).filter(Boolean).forEach(line => {
const idx = line.indexOf(‘#’);
if (idx > 0) {
const remark = line.substring(0, idx);
const token = line.substring(idx + 1);
cacheMap[remark] = token;
}
});
}
} catch (e) {}
return cacheMap;
}

function saveTokenCache(cacheMap) {
try {
const content = Object.entries(cacheMap).map(([r, t]) => ${r}#${t}).join(‘\n’);
fs.writeFileSync(CACHE_FILE, content || ‘#cache’, { encoding: ‘utf8’, flag: ‘w’ });
} catch (e) {}
}

/* ==================== 微信 code 换 token ==================== */
async function getCode(wxid) {
try {
const url = ${WECHAT_SERVER}/api/v1/wx/app/get/code;
const res = await axios.post(url, { wxid, appid: APPID }, { timeout: 10000 });
const code = res.data?.Data?.code;
if (!code) {
console.log(‘ getCode 响应:’, JSON.stringify(res.data));
return null;
}
return code;
} catch (e) {
console.log(‘ getCode 异常:’, e.message);
return null;
}
}

async function codeToToken(code) {
try {
const url = ${BASE_URL}/api/sp-portal/store/icoke/wechat/loginNoCache/${code};
const headers = {
‘Host’: ‘member-api.icoke.cn’,
‘content-type’: ‘application/json’,
‘Accept’: ‘application/json, text/plain, /’,
‘Accept-Encoding’: ‘gzip,compress,br,deflate’,
‘User-Agent’: UA,
‘Referer’: REFERER,
};
const res = await axios.get(url, { headers, timeout: 10000 });
const body = res.data;
// 实际抓包确认:token 在响应体的 jwtString 字段
if (body && body.jwtString) {
return body.jwtString;
}
console.log(‘ codeToToken 响应:’, JSON.stringify(body).substring(0, 300));
return null;
} catch (e) {
console.log(‘ codeToToken 异常:’, e.message);
return null;
}
}

async function getValidToken(remark, wxid, force = false) {
const m = loadTokenCache();
if (force) delete m[remark];
if (m[remark]) {
console.log(‘ 使用缓存 token’);
return m[remark];
}
console.log(‘ 获取微信 code…’);
const code = await getCode(wxid);
if (!code) return null;
console.log(✅ code: ${code.substring(0, 10)}...);
const token = await codeToToken(code);
if (!token) return null;
m[remark] = token;
saveTokenCache(m);
console.log(‘ token 已缓存’);
return token;
}

/* ==================== 判断 token 失效 ==================== */
function isTokenInvalid(data) {
if (!data) return false;
const msg = (data.message || data.msg || ‘’).toLowerCase();
return msg.includes(‘未登录’) || msg.includes(‘token’) || msg.includes(‘登录’) ||
data.code === ‘401’ || data.code === 401 ||
data.status === 401 || data.httpStatus === 401;
}

/* ==================== 公共请求头 ==================== */
function getHeaders(token) {
return {
‘Host’: ‘member-api.icoke.cn’,
‘Authorization’: token,
‘content-type’: ‘application/json’,
‘Accept’: ‘application/json, text/plain, /’,
‘Accept-Encoding’: ‘gzip,compress,br,deflate’,
‘User-Agent’: UA,
‘Referer’: REFERER,
};
}

/* ==================== API 请求封装 ==================== */
async function apiGet(path, token, params) {
try {
const res = await axios.get(${BASE_URL}${path}, {
headers: getHeaders(token),
params,
timeout: 15000,
});
return res.data;
} catch (e) {
console.log(❌ GET ${path} 异常:, e.message);
return null;
}
}

async function apiPost(path, token, data) {
try {
const res = await axios.post(${BASE_URL}${path}, data, {
headers: getHeaders(token),
timeout: 15000,
});
return res.data;
} catch (e) {
console.log(❌ POST ${path} 异常:, e.message);
return null;
}
}

/* ==================== 业务接口 ==================== */

async function getSignOutline(token) {
const res = await apiGet(‘/api/icoke-sign/icoke/mini/sign/main/getSignOutline’, token);
if (!res) return null;
if (isTokenInvalid(res)) return { needRefresh: true };
const today = new Date();
const y = today.getFullYear(), m = today.getMonth() + 1, d = today.getDate();
const list = res.data || ;
const todayRecord = list.find(r => r.year === y && r.month === m && r.day === d);
return {
signed: !!(todayRecord && todayRecord.exist),
totalDays: list.filter(r => r.exist).length,
point: todayRecord?.point || 0,
data: list,
raw: res,
};
}

async function doSign(token) {
const res = await apiGet(‘/api/icoke-sign/icoke/mini/sign/main/sign’, token);
if (!res) return { success: false, msg: ‘请求失败’ };
if (isTokenInvalid(res)) return { needRefresh: true };
if (res.success === true || res.isSuccess === true) {
return {
success: true,
point: res.point || 0,
msg: res.message || ‘签到成功’,
};
}

const msg = res.message || res.msg || JSON.stringify(res);
if (msg.includes(‘已签’) || msg.includes(‘重复’) || msg.includes(‘already’)) {
return { success: false, alreadySigned: true, msg };
}
return { success: false, msg };
}

/** 查询积分 */
async function getPoints(token) {
const res = await apiGet(‘/api/icoke-customer/icoke/mini/customer/main/points’, token);
if (!res) return null;
return {
point: res.point || 0,
experiencePoints: res.experiencePoints || 0,
frozenPoint: res.frozenPoint || 0,
};
}

/** 查询用户基本信息 */
async function getUserInfo(token) {
const res = await apiGet(‘/api/icoke-customer/icoke/mini/customer/main/base/info’, token);
if (!res) return null;
return {
name: res.name || ‘未知’,
mobile: res.mobile || ‘’,
grade: res.grade || ‘’,
};
}

/* ==================== 工具函数 ==================== */
function sleep(ms) {
return new Promise(r => setTimeout(r, ms));
}

/* ==================== 单账号执行 ==================== */
async function runSingleAccount(accountStr, index) {
const sepIdx = accountStr.indexOf(‘#’);
if (sepIdx <= 0) {
console.log(❌ 第${index}个账号格式错误,应为 备注#wxid);
return;
}
const remark = accountStr.substring(0, sepIdx);
const wxid = accountStr.substring(sepIdx + 1).trim();

console.log(\n====================================);
console.log(🚀 【可口可乐】${remark}(第${index}个));
console.log(====================================);

// 获取 token
let token = await getValidToken(remark, wxid);
if (!token) {
console.log(‘ 获取 token 失败,跳过’);
return;
}

// 查询签到状态
let outline = await getSignOutline(token);
if (outline?.needRefresh) {
console.log(‘ token 失效,重新获取…’);
token = await getValidToken(remark, wxid, true);
if (!token) { console.log(‘ 重新获取 token 失败’); return; }
outline = await getSignOutline(token);
}

if (outline) {
if (outline.signed) {
console.log(📅 签到状态:✅ 今日已签到 | 本月已签 ${outline.totalDays} 天);
} else {
console.log(📅 签到状态:❌ 今日未签到 | 本月已签 ${outline.totalDays} 天);
}
}

// 执行签到
let signResult = await doSign(token);
if (signResult?.needRefresh) {
console.log(‘ token 失效,重新获取…’);
token = await getValidToken(remark, wxid, true);
if (!token) { console.log(‘ 重新获取 token 失败’); return; }
signResult = await doSign(token);
}

if (signResult?.success) {
console.log(✅ 签到成功!获得积分:${signResult.point});
} else if (signResult?.alreadySigned) {
console.log(ℹ️ 今日已签到:${signResult.msg});
} else {
console.log(⚠️ 签到结果:${signResult?.msg || '未知'});
}

await sleep(1000);

// 查询积分
const pts = await getPoints(token);
if (pts) {
console.log(💰 当前积分:${pts.point} | 经验值:${pts.experiencePoints} | 冻结:${pts.frozenPoint});
}

// 查询用户信息
const userInfo = await getUserInfo(token);
if (userInfo) {
console.log(👤 会员:${userInfo.name} | 手机:${userInfo.mobile});
}

console.log(✨ ${remark} 执行完成\n);
}

/* ==================== 主函数 ==================== */
!(async () => {
console.log(📢 可口可乐吧签到 - 共检测到 ${wxidList.length} 个账号);
for (let i = 0; i < wxidList.length; i++) {
await runSingleAccount(wxidList[i], i + 1);
if (i < wxidList.length - 1) await sleep(2000);
}
console.log(‘ 全部账号执行完毕!’);
})();

网友解答:
--【壹】--:

这个积分能用来做什么呢,是不是换礼品还是可以换可乐喝


--【贰】--:

WECHAT_SERVER 这个要配什么?


--【叁】--:

佬友有 wechatserver渠道吗,这个有的话可以薅的羊毛就太多了


--【肆】--:

这个帖子有点随性了佬,建议来规范下帖子格式,加个代码引用啥的


--【伍】--:

放在代码里输出吧,另外,这个是啥?可口可乐还有什么活动?


--【陆】--:

我看这也不是二选一呀, 在 wxid 获取 code 时候还是要 wechat server 不然这个脚本没有作用


--【柒】--:

可以考虑弄个文档也可以吧?这样看有点别扭啊


--【捌】--:

感谢佬友的分享,佬友这个积分可以拿来干嘛


--【玖】--:

大佬可以试试用markdown的格式输出,目前这个看起来有点乱。最后感谢分享


--【拾】--:

感谢佬分享 用markdown的代码编辑 可读性比较高


--【拾壹】--: 小旭:

/**

  • 可口可乐吧 签到 - 青龙多账号版
  • 环境变量:
  • WXID: 备注#wxid,多账号用 & 或 换行 分隔
  • 示例: 账号1#wxid_xxxxxxxx&账号2#wxid_yyyyyyyy
  • 功能:
    1. 微信 code → Bearer token(带缓存)
    1. 查询签到状态
    1. 执行每日签到
    1. 查询积分
  • cron: 30 8 * * *
  • const: disabled = false
    */

const axios = require(‘axios’);
const fs = require(‘fs’);
const path = require(‘path’);

/* ==================== 常量配置 ==================== */
const APPID = ‘wxa5811e0426a94686’;
const BASE_URL = ‘https://member-api.icoke.cn’;
const UA = ‘Mozilla/5.0 (iPhone; CPU iPhone OS 16_3_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Mobile/15E148 MicroMessenger/8.0.59(0x18003b2e) NetType/4G Language/zh_CN’;
const REFERER = https://servicewechat.com/${APPID}/499/page-frame.html;

const SCRIPT_DIR = path.dirname(__filename);
const CACHE_FILE = path.join(SCRIPT_DIR, ‘可口可乐签到_cache.txt’);

/* ==================== 环境变量 ==================== */
const WECHAT_SERVER = process.env.WECHAT_SERVER;
const WXID_ENV = process.env.WXID;

if (!WECHAT_SERVER || !WXID_ENV) {
console.log(‘ 请设置 WECHAT_SERVER 和 WXID 环境变量’);
process.exit(1);
}

const wxidList = WXID_ENV.split(/[\n&]/).map(i => i.trim()).filter(i => i && i.includes(‘#’));
if (!wxidList.length) {
console.log(‘ WXID 格式错误,请使用 备注#wxid 格式’);
process.exit(1);
}

/* ==================== 缓存管理 ==================== */
function loadTokenCache() {
const cacheMap = {};
try {
if (fs.existsSync(CACHE_FILE)) {
fs.readFileSync(CACHE_FILE, ‘utf8’).split(/\n/).filter(Boolean).forEach(line => {
const idx = line.indexOf(‘#’);
if (idx > 0) {
const remark = line.substring(0, idx);
const token = line.substring(idx + 1);
cacheMap[remark] = token;
}
});
}
} catch (e) {}
return cacheMap;
}

function saveTokenCache(cacheMap) {
try {
const content = Object.entries(cacheMap).map(([r, t]) => ${r}#${t}).join(‘\n’);
fs.writeFileSync(CACHE_FILE, content || ‘#cache’, { encoding: ‘utf8’, flag: ‘w’ });
} catch (e) {}
}

/* ==================== 微信 code 换 token ==================== */
async function getCode(wxid) {
try {
const url = ${WECHAT_SERVER}/api/v1/wx/app/get/code;
const res = await axios.post(url, { wxid, appid: APPID }, { timeout: 10000 });
const code = res.data?.Data?.code;
if (!code) {
console.log(‘ getCode 响应:’, JSON.stringify(res.data));
return null;
}
return code;
} catch (e) {
console.log(‘ getCode 异常:’, e.message);
return null;
}
}

async function codeToToken(code) {
try {
const url = ${BASE_URL}/api/sp-portal/store/icoke/wechat/loginNoCache/${code};
const headers = {
‘Host’: ‘member-api.icoke.cn’,
‘content-type’: ‘application/json’,
‘Accept’: ‘application/json, text/plain, /’,
‘Accept-Encoding’: ‘gzip,compress,br,deflate’,
‘User-Agent’: UA,
‘Referer’: REFERER,
};
const res = await axios.get(url, { headers, timeout: 10000 });
const body = res.data;
// 实际抓包确认:token 在响应体的 jwtString 字段
if (body && body.jwtString) {
return body.jwtString;
}
console.log(‘ codeToToken 响应:’, JSON.stringify(body).substring(0, 300));
return null;
} catch (e) {
console.log(‘ codeToToken 异常:’, e.message);
return null;
}
}

async function getValidToken(remark, wxid, force = false) {
const m = loadTokenCache();
if (force) delete m[remark];
if (m[remark]) {
console.log(‘ 使用缓存 token’);
return m[remark];
}
console.log(‘ 获取微信 code…’);
const code = await getCode(wxid);
if (!code) return null;
console.log(✅ code: ${code.substring(0, 10)}...);
const token = await codeToToken(code);
if (!token) return null;
m[remark] = token;
saveTokenCache(m);
console.log(‘ token 已缓存’);
return token;
}

/* ==================== 判断 token 失效 ==================== */
function isTokenInvalid(data) {
if (!data) return false;
const msg = (data.message || data.msg || ‘’).toLowerCase();
return msg.includes(‘未登录’) || msg.includes(‘token’) || msg.includes(‘登录’) ||
data.code === ‘401’ || data.code === 401 ||
data.status === 401 || data.httpStatus === 401;
}

/* ==================== 公共请求头 ==================== */
function getHeaders(token) {
return {
‘Host’: ‘member-api.icoke.cn’,
‘Authorization’: token,
‘content-type’: ‘application/json’,
‘Accept’: ‘application/json, text/plain, /’,
‘Accept-Encoding’: ‘gzip,compress,br,deflate’,
‘User-Agent’: UA,
‘Referer’: REFERER,
};
}

/* ==================== API 请求封装 ==================== */
async function apiGet(path, token, params) {
try {
const res = await axios.get(${BASE_URL}${path}, {
headers: getHeaders(token),
params,
timeout: 15000,
});
return res.data;
} catch (e) {
console.log(❌ GET ${path} 异常:, e.message);
return null;
}
}

async function apiPost(path, token, data) {
try {
const res = await axios.post(${BASE_URL}${path}, data, {
headers: getHeaders(token),
timeout: 15000,
});
return res.data;
} catch (e) {
console.log(❌ POST ${path} 异常:, e.message);
return null;
}
}

/* ==================== 业务接口 ==================== */

async function getSignOutline(token) {
const res = await apiGet(‘/api/icoke-sign/icoke/mini/sign/main/getSignOutline’, token);
if (!res) return null;
if (isTokenInvalid(res)) return { needRefresh: true };
const today = new Date();
const y = today.getFullYear(), m = today.getMonth() + 1, d = today.getDate();
const list = res.data || ;
const todayRecord = list.find(r => r.year === y && r.month === m && r.day === d);
return {
signed: !!(todayRecord && todayRecord.exist),
totalDays: list.filter(r => r.exist).length,
point: todayRecord?.point || 0,
data: list,
raw: res,
};
}

async function doSign(token) {
const res = await apiGet(‘/api/icoke-sign/icoke/mini/sign/main/sign’, token);
if (!res) return { success: false, msg: ‘请求失败’ };
if (isTokenInvalid(res)) return { needRefresh: true };
if (res.success === true || res.isSuccess === true) {
return {
success: true,
point: res.point || 0,
msg: res.message || ‘签到成功’,
};
}

const msg = res.message || res.msg || JSON.stringify(res);
if (msg.includes(‘已签’) || msg.includes(‘重复’) || msg.includes(‘already’)) {
return { success: false, alreadySigned: true, msg };
}
return { success: false, msg };
}

/** 查询积分 */
async function getPoints(token) {
const res = await apiGet(‘/api/icoke-customer/icoke/mini/customer/main/points’, token);
if (!res) return null;
return {
point: res.point || 0,
experiencePoints: res.experiencePoints || 0,
frozenPoint: res.frozenPoint || 0,
};
}

/** 查询用户基本信息 */
async function getUserInfo(token) {
const res = await apiGet(‘/api/icoke-customer/icoke/mini/customer/main/base/info’, token);
if (!res) return null;
return {
name: res.name || ‘未知’,
mobile: res.mobile || ‘’,
grade: res.grade || ‘’,
};
}

/* ==================== 工具函数 ==================== */
function sleep(ms) {
return new Promise(r => setTimeout(r, ms));
}

/* ==================== 单账号执行 ==================== */
async function runSingleAccount(accountStr, index) {
const sepIdx = accountStr.indexOf(‘#’);
if (sepIdx <= 0) {
console.log(❌ 第${index}个账号格式错误,应为 备注#wxid);
return;
}
const remark = accountStr.substring(0, sepIdx);
const wxid = accountStr.substring(sepIdx + 1).trim();

console.log(\n====================================);
console.log(🚀 【可口可乐】${remark}(第${index}个));
console.log(====================================);

// 获取 token
let token = await getValidToken(remark, wxid);
if (!token) {
console.log(‘ 获取 token 失败,跳过’);
return;
}

// 查询签到状态
let outline = await getSignOutline(token);
if (outline?.needRefresh) {
console.log(‘ token 失效,重新获取…’);
token = await getValidToken(remark, wxid, true);
if (!token) { console.log(‘ 重新获取 token 失败’); return; }
outline = await getSignOutline(token);
}

if (outline) {
if (outline.signed) {
console.log(📅 签到状态:✅ 今日已签到 | 本月已签 ${outline.totalDays} 天);
} else {
console.log(📅 签到状态:❌ 今日未签到 | 本月已签 ${outline.totalDays} 天);
}
}

// 执行签到
let signResult = await doSign(token);
if (signResult?.needRefresh) {
console.log(‘ token 失效,重新获取…’);
token = await getValidToken(remark, wxid, true);
if (!token) { console.log(‘ 重新获取 token 失败’); return; }
signResult = await doSign(token);
}

if (signResult?.success) {
console.log(✅ 签到成功!获得积分:${signResult.point});
} else if (signResult?.alreadySigned) {
console.log(ℹ️ 今日已签到:${signResult.msg});
} else {
console.log(⚠️ 签到结果:${signResult?.msg || '未知'});
}

await sleep(1000);

// 查询积分
const pts = await getPoints(token);
if (pts) {
console.log(💰 当前积分:${pts.point} | 经验值:${pts.experiencePoints} | 冻结:${pts.frozenPoint});
}

// 查询用户信息
const userInfo = await getUserInfo(token);
if (userInfo) {
console.log(👤 会员:${userInfo.name} | 手机:${userInfo.mobile});
}

console.log(✨ ${remark} 执行完成\n);
}

/* ==================== 主函数 ==================== */
!(async () => {
console.log(📢 可口可乐吧签到 - 共检测到 ${wxidList.length} 个账号);
for (let i = 0; i < wxidList.length; i++) {
await runSingleAccount(wxidList[i], i + 1);
if (i < wxidList.length - 1) await sleep(2000);
}
console.log(‘ 全部账号执行完毕!’);
})();

是微信协议,群里别的佬发的,好像他自己搭建了一个微信协议


--【拾贰】--:

看了一下能兑换的东西不多,用处不大,花精力写这个有点费力不讨好


--【拾叁】--:

没有取code服务是无法运行这个脚本的,wxid好友就能拿到,必须取token才能签到,wxid不能签到啊


--【拾肆】--:

这个签到的积分能干些啥呢 还多个微信弄呀


--【拾伍】--:

这是有什么羊毛活动吗,可口可乐签到,佬能细说下


--【拾陆】--: 小旭:

WECHAT_SERVER

蹲一下,我也好奇,这个有点逆天了,好像是把微信小程序内部接口给逆向出来了吧


--【拾柒】--:

不用管wechatserver,去抓包wxid就行了,二选一

标签:软件开发