Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

@digshare/script

Package Overview
Dependencies
Maintainers
3
Versions
37
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@digshare/script - npm Package Compare versions

Comparing version 0.3.3 to 0.4.0-1

bin/run

8

bld/cli/@core/index.d.ts

@@ -1,1 +0,7 @@

export * from './build';
export * from './pack';
export * from './api';
export * from './config';
export * from './script';
export * from './connect';
export * from './log';
export * from './invoke';
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const tslib_1 = require("tslib");
(0, tslib_1.__exportStar)(require("./build"), exports);
tslib_1.__exportStar(require("./pack"), exports);
tslib_1.__exportStar(require("./api"), exports);
tslib_1.__exportStar(require("./config"), exports);
tslib_1.__exportStar(require("./script"), exports);
tslib_1.__exportStar(require("./connect"), exports);
tslib_1.__exportStar(require("./log"), exports);
tslib_1.__exportStar(require("./invoke"), exports);
//# sourceMappingURL=index.js.map

79

package.json
{
"name": "@digshare/script",
"version": "0.3.3",
"version": "0.4.0-1",
"license": "MIT",
"author": "Chengdu Enverse Technology Co., Ltd.",
"main": "bld/library/index.js",
"types": "bld/library/index.d.ts",
"publishConfig": {
"tag": "next"
},
"exports": {
".": {
"types": "./bld/library/cjs/index.d.ts",
"import": "./bld/library/esm/index.js",
"require": "./bld/library/cjs/index.js"
},
"./x": {
"types": "./bld/x/index.d.ts",
"import": "./bld/x/index.js"
}
},
"bin": {
"digshare-script": "bld/cli/main.js",
"dss": "bld/cli/main.js"
"dss": "./bin/run"
},
"scripts": {
"build": "rimraf ./bld && tsc --build",
"lint": "eslint .",
"lint": "run-in-every eslint-project --parallel --echo -- eslint --config {configFileName} --no-error-on-unmatched-pattern .",
"lint-prettier": "prettier --check .",
"test": "yarn lint-prettier && yarn build && yarn lint && yarn bundle-types",
"bundle-types": "dts-bundle-generator --out-file bundled-types.d.ts bld/library/index.d.ts",
"prepublishOnly": "yarn test"
"test": "yarn lint-prettier && yarn build && yarn lint"
},
"oclif": {
"bin": "dss",
"dirname": "dss",
"commands": "./bld/cli/commands"
},
"dependencies": {
"@rollup/plugin-commonjs": "^22.0.0-4",
"@rollup/plugin-json": "^4.1.0",
"@rollup/plugin-node-resolve": "^13.1.1",
"@types/node": "^16.11.11",
"@types/node-fetch": "^2.5.12",
"bson": "^4.6.0",
"clime": "^0.5.14",
"node-fetch": "2.6.7",
"rollup": "^2.62.0",
"rollup-plugin-terser": "^7.0.2",
"tslib": "^2.3.1"
"@oclif/core": "^2",
"@oclif/plugin-help": "^5",
"@rollup/plugin-commonjs": "^24.0.1",
"@rollup/plugin-json": "^6.0.0",
"@rollup/plugin-node-resolve": "^15.0.1",
"@rollup/plugin-terser": "^0.4.1",
"@rollup/plugin-virtual": "^3.0.1",
"@types/ms": "^0.7.31",
"@types/prompts": "^2.4.3",
"chalk": "4",
"enhanced-resolve": "^5.12.0",
"entrance-decorator": "^0.2.2",
"ms": "^2.1.3",
"open": "^8.4.2",
"pkg-dir": "5",
"prompts": "^2.4.2",
"rollup": "^3.19.1",
"tslib": "^2.5.0",
"undici": "^5.20.0",
"x-value": "^0.1.11"
},
"devDependencies": {
"@mufan/code": "^0.2.10",
"@mufan/eslint-plugin": "^0.1.48",
"dts-bundle-generator": "^6.5.0",
"eslint": "^8.10.0",
"prettier": "^2.5.1",
"rimraf": "^3.0.2",
"typescript": "^4.5.5"
"@mufan/code": "^0.2.16",
"@mufan/eslint-plugin": "^0.1.77",
"@types/node": "^18.15.0",
"eslint": "^8.36.0",
"prettier": "^2.8.4",
"rimraf": "^4.4.0",
"run-in-every": "^0.2.0",
"typescript": "^4.9.5"
}
}
# 盯梢 {script}
盯梢是可以将消息变现的推送工具,盯梢频道主既可以手动向订阅者推送新消息,也可以通过盯梢脚本自动完成消息采集和推送。
```ts
import {script} from '@digshare/script';
盯梢脚本是盯梢提供的自动化频道消息推送方案,频道主可以通过 JavaScript 快速编写自动化脚本,托管到盯梢脚本服务器,定时或通过 webhook 触发执行。
export default script(async (state = 0) => {
state++;
盯梢脚本提供了两种编辑/部署方案:
1. 通过在线[盯梢脚本编辑器(script.dingshao.cn)](https://script.dingshao.cn/)直接编辑和部署脚本。
2. 通过 npm 命令行向盯梢脚本 registry 发布脚本。
## 为盯梢频道创建脚本
1. 打开[盯梢手机应用](https://www.dingshao.cn/),切换到“发布”页面创建频道。
2. 打开[盯梢脚本编辑器](https://script.dingshao.cn/),在手机应用上进入频道管理页面,打开扫一扫授权盯梢脚本编辑器。
此时可以看到生成的默认脚本,大致如下:
```js
/**
* @param payload webhook 参数(POST body,支持 JSON)
* @param context 脚本执行上下文,包括简单的键值存储对象
*/
export default async function (payload, context) {
return {
content: '你好,盯梢!',
message: `这是脚本自动发送的第 ${state} 条消息!`,
state,
};
}
});
```
> 目前盯梢脚本编辑器提供了 JavaScript 和 TypeScript 两个版本的模板,可自行切换。
## License
在脚本“调试”页面点击右下角“测试”按钮即可在模拟环境下执行脚本,此时不会真的推送消息。
调试完成后,可以在部署页面看到 webhook 或按需配置计划执行,点击右下角“部署”按钮即可实际部署脚本。部署完成后,按钮将变为“执行”,再次点击可以手动执行部署脚本,此时如果脚本函数返回值不为空,则会推送相关消息到当前频道。
[在线编辑器提供了少量 npm 包](https://docs.dingshao.cn/script-manual/online-editor/available-npm-packages),同时提供了全局的 `fetch` API(`node-fetch`)供开发者使用。如果您想使用更多 npm 包,建议通过本地构建上传,参考[盯梢脚本模板](https://github.com/digshare/digshare-script-template)。
## 使用参考
### 脚本 `default` 函数
脚本 `default` 函数类型为 `Script`,可以是普通的异步函数或异步生成器。
`Script` 接受两个参数,`payload` 和 `context`:
- `payload` 是用户通过 webhook 传入的参数。
- `context` 类型为 `ScriptContext`。
- 返回值(或 `yield` 值)为消息对象 `ScriptMessage`,包含消息内容、链接、标签等。
### 脚本上下文 `ScriptContext<TStorage extends object>`
[[源码]](./src/library/script/context.ts)
`ScriptContext` 中最常用的是 `storage`,该对象提供了 `getItem(key)` 和 `setItem(key, value)` 两个方法,可以用于存储脚本执行信息,经常被我们用来避免发送重复的消息或内容。
```js
export default async function (payload, {storage}) {
let count = storage.getItem('count') ?? 1;
console.log(`这是脚本第 ${count} 次执行。`);
storage.setItem('count', count + 1);
return {
content: '你好,盯梢!',
};
}
```
### 脚本消息 `ScriptMessage`
[[源码]](./src/library/script/script.ts)
除了 `content` 之外都是可选参数。
```js
export default async function (payload, context) {
if (nothingNew) {
// 返回 undefined 表示不用推送新消息。
return undefined;
}
return {
content: '消息内容。',
images: [
/* 图片 Buffer 的数组。*/
],
links: [
'https://www.dingshao.cn/',
{
url: 'https://script.dingshao.cn/',
description: '盯梢脚本编辑器',
},
],
tags: ['标签', '不存在将自动创建'],
};
}
```
## 本地开发
盯梢脚本支持本地开发构建后,通过 npm 发布到盯梢脚本 registry。
参考[盯梢脚本模板](https://github.com/digshare/digshare-script-template)。
MIT License.

@@ -1,1 +0,7 @@

export * from './build';
export * from './pack';
export * from './api';
export * from './config';
export * from './script';
export * from './connect';
export * from './log';
export * from './invoke';

@@ -1,105 +0,66 @@

import {serialize} from 'bson';
import fetch from 'node-fetch';
import {ReadableStream} from 'stream/web';
import {devLog} from './@utils';
import type {Response} from 'undici';
export interface PublishMessageOptions {
content: string;
open?: boolean;
images?: Buffer[];
links?: (
| string
| {
url: string;
title?: string;
image?: string;
description?: string;
}
)[];
tags?: string[] | undefined;
clientId?: string;
}
export class API {
constructor(readonly endpoint: string, readonly accessToken: string) {}
export interface IScriptAPI<TData extends object = object> {
baseURL: string;
getStorage(): Promise<Partial<TData>>;
updateStorage(data: Partial<TData>): Promise<void>;
publishMessage(options: PublishMessageOptions): Promise<void>;
}
async call<TReturn extends object>(
path: string,
params: object,
file?: ArrayBuffer | Blob | ReadableStream,
): Promise<TReturn> {
const {endpoint, accessToken} = this;
export interface ScriptAPIOptions {
baseURL?: string;
timeout?: number;
}
const url = `${endpoint}${path}`;
export class ScriptAPI<TData extends object> implements IScriptAPI<TData> {
get baseURL(): string {
return this.options.baseURL ?? 'https://api.dingshao.cn/v1';
}
constructor(private token: string, private options: ScriptAPIOptions = {}) {}
async getStorage(): Promise<Partial<TData>> {
let {storage} = Object(await this.call('/channel-script/get-storage', {}));
return storage ?? {};
}
async updateStorage(storage: Partial<TData>): Promise<void> {
await this.call('/channel-script/update-storage', {
storage,
});
}
async publishMessage(params: PublishMessageOptions): Promise<void> {
await this.call('/channel/publish-message', serialize(params), {
'content-type': 'application/bson',
});
}
private call<T>(path: string, data: any, headers?: any): Promise<T> {
let {timeout} = this.options;
headers = {
'content-type': 'application/json',
authorization: this.token,
...headers,
const accessTokenHeaderField = accessToken && {
'x-access-token': accessToken,
};
return fetch(`${this.baseURL}${path}`, {
method: 'post',
body:
headers?.['content-type'] === 'application/json'
? JSON.stringify(data)
: data,
headers,
timeout,
})
.then(res => res.json())
.then(json => {
if (json.error) {
throw Error(JSON.stringify(json.error, undefined, 2));
}
let response: Response;
return json.value;
if (file) {
response = await fetch(url, {
method: 'POST',
headers: {
'content-type': 'application/octet-stream',
'x-body': JSON.stringify(params),
...accessTokenHeaderField,
},
body: file,
duplex: file instanceof ReadableStream ? 'half' : undefined,
});
}
}
} else {
response = await fetch(url, {
method: 'POST',
headers: {
'content-type': 'application/json',
...accessTokenHeaderField,
},
body: JSON.stringify(params),
});
}
export class DevScriptAPI<TData extends object> implements IScriptAPI<TData> {
baseURL = 'http://dingshare.dev';
const {status} = response;
constructor(private storage: TData) {}
if (status !== 200) {
throw new Error(`状态码错误: ${status}`);
}
async getStorage(): Promise<TData> {
return this.storage;
}
const ret = (await response.json()) as
| {error: {code: string; message: string}}
| {value: TReturn};
async updateStorage<TStore>(storage: TStore): Promise<void> {
devLog('updateStorage', storage);
}
if ('error' in ret) {
const {
error: {code, message},
} = ret;
throw new Error(`${code}: ${message}`);
} else {
const {value} = ret;
async publishMessage(params: PublishMessageOptions): Promise<void> {
devLog('publishMessage', params);
return value;
}
}
}
export * from './api';
export * from './script';
export * from './storage';
export * from './context';
export * from './dev-run';
export * from './types';

@@ -1,44 +0,172 @@

import {ScriptContext} from './context';
import {isGeneratorObject} from 'util/types';
export interface ScriptMessage {
/**
* 消息内容。
*/
content: string;
/**
* 是否为公开消息?当前仅对收费频道有效。
*/
open?: boolean;
/**
* 消息图片数组。
*/
images?: Buffer[];
/**
* 消息链接,可以是 URL 或对象的数组。
*/
links?: (
| string
| {
url: string;
title?: string;
image?: string;
description?: string;
import type {ScriptUpdateMessage} from '@digshare/script/x';
import {API} from './api';
export interface ScriptUpdate<TState> {
message?: ScriptUpdateMessage | string;
state?: TState;
}
export type ScriptProgram<TState> = (
state: TState | undefined,
) =>
| ScriptUpdate<TState>
| void
| Promise<ScriptUpdate<TState> | void>
| Generator<ScriptUpdate<TState>, void, void>
| AsyncGenerator<ScriptUpdate<TState>, void, void>;
export class Script<TState> {
private api: API | undefined;
private dryRun = false;
constructor(readonly program: ScriptProgram<TState>) {}
async configure({
endpoint,
accessToken,
dryRun,
}: ScriptConfigureOptions): Promise<void> {
this.api = new API(endpoint, accessToken);
this.dryRun = dryRun;
}
async run({state}: ScriptRunOptions<TState>): Promise<TState | void> {
const {program} = this;
const updates = program(state);
if (!updates) {
return;
}
if (typeof updates === 'object') {
if (updates instanceof Promise) {
const update = await updates;
if (update) {
await this.update(update);
}
} else if (
(
isGeneratorObject as (
object: unknown,
) => object is Generator | AsyncGenerator
)(updates)
) {
for await (const update of updates) {
await this.update(update);
}
} else {
await this.update(updates);
}
)[];
tags?: string[] | undefined;
} else {
throw new Error('无效的脚本返回值');
}
}
private async update({
message: updateMessage,
state,
}: ScriptUpdate<TState>): Promise<void> {
const {api, dryRun} = this;
if (!api) {
throw new Error('API 未配置');
}
if (typeof updateMessage === 'string') {
updateMessage = {content: updateMessage};
}
if (updateMessage) {
console.info('发布消息', updateMessage);
}
if (state !== undefined) {
console.info('更新状态', state);
}
if (dryRun) {
return;
}
if (!updateMessage && state === undefined) {
return;
}
let message: ScriptMessage | undefined;
if (updateMessage) {
const {title, content, images} = updateMessage;
let imageURLs: string[] | undefined;
if (images) {
imageURLs = [];
for (let image of images) {
if (typeof image === 'string') {
console.info('请求图片', image);
const response = await fetch(image);
if (!response.ok) {
throw new Error('图片请求失败');
}
const type = response.headers.get('content-type');
if (!type || !type.startsWith('image/')) {
throw new Error('无效的图片类型');
}
image = response.body!;
}
const {url} = await api.call<{url: string}>(
'/v2/channel/upload-content-image',
{},
image,
);
imageURLs.push(url);
}
}
message = {
title,
content,
images: imageURLs,
};
}
await api.call('/v2/channel/script-update', {
message,
state,
});
}
}
export type Script<TPayload, TStorage extends object> = (
payload: TPayload,
context: ScriptContext<TStorage>,
) => Promise<ScriptMessage | void> | AsyncGenerator<ScriptMessage, void>;
export interface ScriptConfigureOptions {
endpoint: string;
accessToken: string;
dryRun: boolean;
}
/**
* 类型辅助函数,无其他用途
* @param fn<payload, storage>
*/
export function script<TPayload, TStorage extends object>(
fn: Script<TPayload, TStorage>,
): Script<TPayload, TStorage> {
return fn;
export interface ScriptRunOptions<TState> {
state: TState | undefined;
}
export function script<TState>(program: ScriptProgram<TState>): Script<TState> {
return new Script(program);
}
export interface ScriptMessage {
title: string | undefined;
content: string;
images: string[] | undefined;
}

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc