集成 Crisp
在 <script>
即可为 Angela 启用 Crisp。
此外,通过
以下是一个示例实现,将全部代码复制到 extension.html
即可启用进阶 Crisp 集成:
<script type="module">
/** @import { Observable } from "rxjs" */
/** @import { User } from "../../../../../angela/src/app/core/user/user.model" */
/** @import { Plan } from "../../../../../angela/src/app/core/plan/plan.model" */
/**
* @typedef AngelaAccessPoints
* @property {() => Observable} watchCommands
* @property {(command) => object} readCommandMetadata
* @property {() => Observable} watchEvents
* @property {(event) => object} readEventMetadata
* @property {() => Observable<string>} queryAuthToken
* @property {() => Observable<User>} queryUser
* @property {() => Observable<Plan | null>} queryUserPlan
*/
/**
* @typedef CrispSdk
* @property {(args: [string, string, [unknown]]) => void} push
* @property {boolean} [adapted]
* @see https://docs.crisp.chat/guides/chatbox-sdks/web-sdk/dollar-crisp/
*/
/**
* @typedef {AngelaAccessPoints & { $crisp?: CrispSdk }} Global
*/
import * as mRxJS from 'https://cdn.skypack.dev/rxjs@7.8.1';
import * as mFilesize from 'https://cdn.skypack.dev/filesize@10.0.12';
import * as mDayjs from 'https://cdn.skypack.dev/dayjs@1.11.9/esm';
/**@returns {asserts v is Global} */
function typeAssertGlobal(v) {}
/**@returns {asserts m is typeof import("rxjs")} */
function typeAssertModuleRxJS(m) {}
/**@returns {asserts m is typeof import("filesize")} */
function typeAssertModuleFilesize(m) {}
/**@returns {asserts m is { default: typeof import('dayjs/esm') }} */
function typeAssertModuleDayjs(m) {}
typeAssertGlobal(window);
const global = window;
const { queryAuthToken, queryUser, queryUserPlan } = global;
typeAssertModuleRxJS(mRxJS);
const { switchMap, combineLatest, timer, first, map } = mRxJS;
typeAssertModuleDayjs(mDayjs);
const dayjs = mDayjs.default;
typeAssertModuleFilesize(mFilesize);
const { filesize } = mFilesize;
timer(1000, 1000)
.pipe(
map(() => global.$crisp),
first(Boolean),
)
.subscribe((crisp) => {
adaptCrisp(crisp);
crisp.adapted = true;
});
/**
* @param {CrispSdk} crisp
*/
function adaptCrisp(crisp) {
queryAuthToken()
.pipe(
switchMap((token) => {
if (!token) return [];
const user$ = queryUser();
const userPlan$ = queryUserPlan();
return combineLatest([user$, userPlan$]);
}),
)
.subscribe(([user, plan]) => {
provideCrispData(crisp, user, plan);
});
}
/**
* @param {CrispSdk} crisp
* @param {User} user
* @param {?Plan} plan
*/
function provideCrispData(crisp, user, plan) {
const formatFilesize = (/**@type {number} */ size) =>
filesize(size, { base: 2, standard: 'jedec', spacer: '' });
const formatDate = (/**@type {Date} */ date) =>
dayjs(date).format('YYYY-MM-DD');
const planExpiry = user.planExpireAt;
const trafficTotal = user.trafficTotal;
const trafficUsed = user.trafficUploaded + user.trafficDownloaded;
crisp.push(['set', 'user:email', [user.email]]);
if (user.avatarUrl) crisp.push(['set', 'user:avatar', [user.avatarUrl]]);
/**@type {[string, string][]} */
const data = [];
data.push(['UUID', user.id]);
data.push(['Plan', plan?.name || 'N/A']);
data.push(['ExpireTime', planExpiry ? formatDate(planExpiry) : 'N/A']);
data.push(['Traffic', formatFilesize(trafficTotal)]);
data.push(['TrafficUsed', formatFilesize(trafficUsed)]);
data.push(['Balance', user.balance.toString()]);
data.push(['Commission', user.commission.toString()]);
data.push(['CommissionPending', user.commissionInProgress.toString()]);
data.push(['CommissionTotal', user.commissionTotal.toString()]);
data.push(['RegistrationTime', formatDate(user.createdAt)]);
crisp.push(['set', 'session:data', [data]]);
}
</script>