AppsFlyerUtil.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345
  1. const Global = require("Global");
  2. const BaseDefine = require("BaseDefine");
  3. var CGPLoginMission = require("CGPLoginMission");
  4. const KEY_MAP = new Map([
  5. ["org.queen.test", "aoRrdANBXaid2HX77FxE6P"],
  6. ["com.teempatti.championplayer", "aoRrdANBXaid2HX77FxE6P"],
  7. ["com.teempatti.Victory", "aoRrdANBXaid2HX77FxE6P"],
  8. ["com.teempatti.qexceed", "aoRrdANBXaid2HX77FxE6P"],
  9. ["com.teempatti.reward", "aoRrdANBXaid2HX77FxE6P"],
  10. ["com.teempatti.qplayer", "aoRrdANBXaid2HX77FxE6P"],
  11. ["com.teempatti.q3acard", "aoRrdANBXaid2HX77FxE6P"],
  12. ["com.teenpatti.queen", "aoRrdANBXaid2HX77FxE6P"],
  13. ["com.teenpatti.queen2", "aoRrdANBXaid2HX77FxE6P"],
  14. ["com.teempatti.qnobles", "aoRrdANBXaid2HX77FxE6P"],
  15. ["com.teempatti.qgrandparty", "aoRrdANBXaid2HX77FxE6P"],
  16. ["com.teempatti.queen", "aoRrdANBXaid2HX77FxE6P"],
  17. ["com.teempatti.royalclub", "aoRrdANBXaid2HX77FxE6P"],
  18. ["com.teempatti.queencard", "aoRrdANBXaid2HX77FxE6P"],
  19. ["com.teempatti.queensnice", "aoRrdANBXaid2HX77FxE6P"],
  20. ["com.teempatti.queensplay", "aoRrdANBXaid2HX77FxE6P"],
  21. ["com.teempatti.queensclub", "aoRrdANBXaid2HX77FxE6P"],
  22. ["com.teempatti.queenmultiplayer", "aoRrdANBXaid2HX77FxE6P"],
  23. ["com.teempatti.queenspokergame", "aoRrdANBXaid2HX77FxE6P"],
  24. ["com.teempatti.queensmaster", "aoRrdANBXaid2HX77FxE6P"],
  25. ["com.teempatti.queenchampion", "aoRrdANBXaid2HX77FxE6P"],
  26. ["com.teempatti.queenbwinner", "aoRrdANBXaid2HX77FxE6P"],
  27. ["com.teempatti.queenwinnerking", "aoRrdANBXaid2HX77FxE6P"],
  28. ["com.teempatti.queenluckypokert", "aoRrdANBXaid2HX77FxE6P"],
  29. ["com.teempatti.queen3pattigame", "aoRrdANBXaid2HX77FxE6P"],
  30. ["com.teenpatti.qindianpoker", "aoRrdANBXaid2HX77FxE6P"],
  31. ["com.teenpattiq.scratchcard", "aoRrdANBXaid2HX77FxE6P"],
  32. ["com.teenpatti.qluckylottery", "aoRrdANBXaid2HX77FxE6P"],
  33. ["com.tp3pattiq.dailylottery", "aoRrdANBXaid2HX77FxE6P"],
  34. ["com.tpq3pattiwin.lottery", "aoRrdANBXaid2HX77FxE6P"],
  35. ["com.indiantp3patti.winner", "aoRrdANBXaid2HX77FxE6P"]
  36. ]);
  37. const ONCE_LINK_KEY_MAP = new Map([
  38. ["com.teempatti.championplayer", "icUo"],
  39. ["com.teempatti.Victory", "9oZ5"],
  40. ["com.teempatti.qexceed", "uUWu"],
  41. ["com.teempatti.reward", "yAEo"],
  42. ["com.teempatti.qplayer", "vt7B"],
  43. ["com.teempatti.q3acard", "H0vK"],
  44. ["com.teenpatti.queen", "vKDF"],
  45. ["com.teenpatti.queen2", "x8wa"],
  46. ["com.teempatti.qnobles", "SWkn"],
  47. ["com.teempatti.qgrandparty", "S4jo"],
  48. ["com.teempatti.queen", "k0Nz"],
  49. ["com.teempatti.royalclub", "S87x"],
  50. ["com.teempatti.queencard", "QmIA"],
  51. ["com.teempatti.queensnice", "ilFV"],
  52. ["com.teempatti.queensplay", "apzC"],
  53. ["com.teempatti.queensclub", "cF9a"],
  54. ["com.teempatti.queenmultiplayer", "sgqo"],
  55. ["com.teempatti.queenspokergame", "BCbX"],
  56. ["com.teempatti.queensmaster", "r3Qy"],
  57. ["com.teempatti.queenchampion", "oK5f"],
  58. ["com.teempatti.queenbwinner", "VXsS"],
  59. ["com.teempatti.queenluckypokert", "00dH"],
  60. ["com.teempatti.queen3pattigame", "CzNs"],
  61. ["com.teenpatti.qindianpoker", "Q1zG"],
  62. ["com.teenpattiq.scratchcard", "E9hj"],
  63. ["com.teenpatti.qluckylottery", "hp1J"],
  64. ]);
  65. var EventQueue = new Array();
  66. class AppFlyerUtil {
  67. constructor() {
  68. this.ONCE_LINK_KEY = "";
  69. this.APPS_FLYER_KEY = "";
  70. }
  71. /**
  72. * 初始化AppsFlyer,根据qq当前包名设置对应key
  73. */
  74. init() {
  75. // let packageName = jsb.reflection.callStaticMethod("org/cocos2dx/javascript/Native", "getAppId", "()Ljava/lang/String;");
  76. let key = "aoRrdANBXaid2HX77FxE6P";//KEY_MAP.get(packageName);
  77. Global.print("AppsFlyer::初始化:(" + key + ")");
  78. if (key) {
  79. // 生成回调
  80. let successEvent = "AppsFlyerConversion";
  81. let errorEvent = "AppsFlyerConversionError";
  82. let onSuccess = () => {
  83. let data = this.getConversionData();
  84. let loginMission = new CGPLoginMission({ URL: cc.vv.config.Address, Port: cc.vv.config.Port });
  85. loginMission.sendAdjustADParam(
  86. `media_source :${data.get("media_source") ? data.get("media_source") : ''}`
  87. + `,campaign :${data.get("campaign") ? data.get("campaign") : ''}`
  88. + `,campaign_id :${data.get("campaign_id") ? data.get("campaign_id") : ''}`);
  89. cc.game.off(errorEvent, onError, this);
  90. // 未初始化前触发的事件重发机制
  91. var interval = setInterval(() => {
  92. if (EventQueue.length > 0) {
  93. let [eventType, parameters] = EventQueue.pop();
  94. this.logEvent(eventType, parameters);
  95. } else {
  96. clearInterval(interval);
  97. }
  98. }, 500);
  99. }
  100. let onError = (s) => {
  101. Global.print(`AppsFlyer::归因失败:(${s})`);
  102. cc.game.off(successEvent, onSuccess, this);
  103. // cc.userOperate.sendRecord("Attribution failure");
  104. setTimeout(() => {
  105. cc.vv.reflection.initAppsFlyer(key, successEvent, errorEvent);
  106. }, 5000)
  107. }
  108. cc.game.once(errorEvent, onError, this);
  109. cc.game.once(successEvent, onSuccess, this);
  110. cc.vv.reflection.initAppsFlyer(key, successEvent, errorEvent);
  111. this.APPS_FLYER_KEY = key;
  112. }
  113. this.setCurrencyCode("INR");
  114. }
  115. /**
  116. * 获取本地归因数据,据网络情况,时间不同,未归因完成时,Native返回空字符
  117. * @returns {Map}
  118. */
  119. getConversionData() {
  120. let jsonStr = cc.vv.reflection.appsFlyerGetConversionData();
  121. Global.print(`AppsFlyer::获取本地归因数据`);
  122. if (jsonStr) {
  123. try {
  124. let json = JSON.parse(jsonStr);
  125. if (json.campaign) {
  126. let attr = new Map();
  127. for (const [key, value] of Object.entries(json)) {
  128. attr.set(key, value);
  129. Global.print(` (${key}: ${value})`);
  130. }
  131. return attr;
  132. } else {
  133. setTimeout(() => {
  134. this.getConversionData();
  135. }, 300000);
  136. }
  137. } catch (error) {
  138. Global.print("AppsFlyer::归因数据解析失败");
  139. }
  140. }
  141. return new Map();
  142. }
  143. /**
  144. * 获取本地归因数据(异步)
  145. * @returns {Promise}
  146. */
  147. getConversionDataAsync() {
  148. Global.print(`AppsFlyer::获取本地归因数据(异步)`);
  149. const INTERVAL_TIME = 5000;
  150. const MAX_REQ_COUNT = 12;
  151. return new Promise((resolve, reject) => {
  152. if (BaseDefine.GAME_PLATFORM === BaseDefine.WEB_H5_PLATFORM) {
  153. resolve(new Map());
  154. return;
  155. }
  156. let count = 0;
  157. let waitFunc = () => {
  158. if (count >= MAX_REQ_COUNT) {
  159. reject("获取超时");
  160. return;
  161. }
  162. let data = this.getConversionData();
  163. if (!data) {
  164. setTimeout(waitFunc, INTERVAL_TIME);
  165. count++;
  166. Global.print(`AppsFlyer::获取本地归因数据(异步)第${count + 1}次`);
  167. } else {
  168. resolve(data);
  169. }
  170. }
  171. waitFunc();
  172. });
  173. }
  174. /**
  175. * 设置AppsFlyer的全局的货币Code, 默认INR(印尼)
  176. */
  177. setCurrencyCode(code) {
  178. code = code || "INR";
  179. Global.print("AppsFlyer::设置全局货币:(" + code + ")");
  180. cc.vv.reflection.appsFlyerSetCurrencyCode(code);
  181. }
  182. /**
  183. * 向AppsFlyer发送事件
  184. * @param eventType 事件类型
  185. * @param {Map} parameters 事件参数
  186. */
  187. logEvent(eventType, parameters = new Map()) {
  188. // AppsFlyer内置的事件类型及参数字段:https://support.appsflyer.com/hc/zh-cn/articles/115005544169
  189. // map to JSON
  190. if (BaseDefine.GAME_PLATFORM === BaseDefine.WEB_H5_PLATFORM) {
  191. return;
  192. }
  193. if (!this.APPS_FLYER_KEY) {
  194. EventQueue.push([eventType, parameters]);
  195. Global.print(`AppsFlyer::事件发送失败:(${eventType})(未初始化)`);
  196. return;
  197. }
  198. let obj = new Object();
  199. for (const [key, value] of parameters) {
  200. obj[key] = value;
  201. }
  202. let json = JSON.stringify(obj);
  203. // 生成回调
  204. let successEvent = "AppsFlyerLog";
  205. let errorEvent = "AppsFlyerLogError";
  206. let onSuccess = () => {
  207. Global.print(`AppsFlyer::事件发送成功:(${eventType})`);
  208. cc.game.off(errorEvent, onError, this);
  209. }
  210. let onError = (s) => {
  211. Global.print(`AppsFlyer::事件发送失败:(${eventType})(${s})`);
  212. cc.game.off(successEvent, onSuccess, this);
  213. }
  214. cc.game.once(successEvent, onSuccess, this);
  215. cc.game.once(errorEvent, onError, this);
  216. cc.vv.reflection.appsFlyerLogEvent(eventType, json, successEvent, errorEvent);
  217. }
  218. /**
  219. * 设置OneLink的模板ID,用于链接跳转地址
  220. */
  221. setAppInviteOneLink() {
  222. let packageName = jsb.reflection.callStaticMethod("org/cocos2dx/javascript/Native", "getAppId", "()Ljava/lang/String;");
  223. let key = ONCE_LINK_KEY_MAP.get(packageName);
  224. if (key) {
  225. Global.print("AppsFlyer::设置OnceLink模板ID:(" + key + ")");
  226. cc.vv.reflection.appsFlyerSetAppInviteOnceLink(key);
  227. this.ONCE_LINK_KEY = key;
  228. }
  229. }
  230. /**
  231. * 获取AppsFlyer跳转的分享链接(异步),生成失败返回长链接,依旧可以使用,但是会暴露参数
  232. * @returns {Promise}
  233. */
  234. generateShareLinkAsync() {
  235. return new Promise((resolve, reject) => {
  236. if (BaseDefine.GAME_PLATFORM === BaseDefine.WEB_H5_PLATFORM) {
  237. resolve(BaseDefine.SHARE_URL);
  238. return;
  239. }
  240. let shareLink = cc.sys.localStorage.getItem("AgentShareLink");
  241. if (shareLink && !!shareLink.match(/onelink\.me/i)) {
  242. resolve(shareLink);
  243. return;
  244. }
  245. //设置模板ID,必填
  246. if (!this.ONCE_LINK_KEY) {
  247. this.setAppInviteOneLink();
  248. }
  249. let myUserID = cc.vv.globalUserInfo.getGameID();
  250. Global.print(`AppsFlyer::生成分享链接:(userID:${myUserID})`);
  251. // 生成回调
  252. let onSuccess = (s) => {
  253. Global.print(`AppsFlyer::生成分享链接成功:(${s})`);
  254. resolve(s);
  255. cc.sys.localStorage.setItem("AgentShareLink", s);
  256. cc.game.off("generateLinkError", onError, this);
  257. }
  258. let onError = (s) => {
  259. Global.print(`AppsFlyer::生成分享链接失败`);
  260. resolve(s);
  261. cc.game.off("generateLinkSuccess", onSuccess, this);
  262. }
  263. cc.game.once("generateLinkSuccess", onSuccess, this);
  264. cc.game.once("generateLinkError", onError, this);
  265. cc.vv.reflection.appsFlyerInitShareLinkGenerator("Share"); // 设置分享方式并初始化链接生成器, 必填
  266. // cc.vv.reflection.appsFlyerSetCampaignOnShareLinkGenerator("100"); // 设置链接渠道
  267. cc.vv.reflection.appsFlyerSetReferrerCustomerIdOnShareLinkGenerator(myUserID); // 设置分享玩家ID
  268. cc.vv.reflection.appsFlyerGenerateLink("generateLinkSuccess", "generateLinkError"); // 生成链接
  269. });
  270. }
  271. /**
  272. * 获取AppsFlyer的唯一标识
  273. */
  274. getAppsFlyerID() {
  275. return cc.vv.reflection.appsFlyerGetAppsFlyerID();
  276. }
  277. /**
  278. * 设置该用户的AppsFlyer自定义玩家ID
  279. */
  280. setCustomerUserID(id) {
  281. cc.vv.reflection.appsFlyerSetCustomerUserID(id);
  282. }
  283. /**
  284. * 获取该用户的AppsFlyer自定义玩家ID
  285. */
  286. getCustomerUserID() {
  287. return cc.vv.reflection.appsFlyerGetCustomerUserID();
  288. }
  289. /**
  290. * 通过AppsFlyer更新当前玩家的渠道及上级数据
  291. */
  292. updateSpreadAndAdChannel(userID) {
  293. this.getConversionDataAsync()
  294. .then((data) => {
  295. let spreadID = data.get("af_referrer_customer_id") || 0; // 分享者用户ID
  296. let campaign = data.get("campaign") || ""; // 广告渠道名称
  297. let channelID = 0;
  298. // 匹配 * 符号以后的数字,不包括 * 符号,且不包括被匹配数字后从非数字字符下标起的任意字符,包括数字
  299. try {
  300. channelID = campaign.match(/\*\d+/)[0].slice(1);
  301. } catch (error) {
  302. Global.print("AppsFlyer::解析渠道错误(" + error + ")");
  303. } finally {
  304. Global.print(`AppsFlyer::updateSpreadAndAdChannel: spread=(${spreadID}), channel=${channelID}, campaign=${campaign}`);
  305. let loginMission = new CGPLoginMission({ URL: cc.vv.config.Address, Port: cc.vv.config.Port });
  306. loginMission.updateSpreadAndChannel(userID, channelID, spreadID);
  307. }
  308. })
  309. .catch((error) => {
  310. Global.print(`AppsFlyer::updateSpreadAndAdChannel: 更新渠道失败(${error})`);
  311. })
  312. }
  313. /**
  314. * 绑定服务器UserID 与 AF 的 AFId
  315. */
  316. bindUserIDAndAFID() {
  317. let loginMission = new CGPLoginMission({ URL: cc.vv.config.Address, Port: cc.vv.config.Port });
  318. this.getAppsFlyerID() && loginMission.bindUserIDAndAFID(this.getAppsFlyerID());
  319. }
  320. }
  321. module.exports = new AppFlyerUtil();