@hesed/gchat 0.3.1 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +37 -7
- package/dist/commands/gchat/config/add-token.js +3 -1
- package/dist/commands/gchat/config/add-user.d.ts +10 -0
- package/dist/commands/gchat/config/add-user.js +20 -0
- package/dist/commands/gchat/config/set-key.js +3 -1
- package/dist/commands/gchat/create-message.d.ts +1 -0
- package/dist/commands/gchat/create-message.js +16 -2
- package/dist/commands/gchat/reply-message.d.ts +1 -0
- package/dist/commands/gchat/reply-message.js +16 -2
- package/dist/config.d.ts +4 -1
- package/dist/config.js +74 -6
- package/oclif.manifest.json +60 -3
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -26,7 +26,7 @@ $ npm install -g @hesed/gchat
|
|
|
26
26
|
$ gchat COMMAND
|
|
27
27
|
running command...
|
|
28
28
|
$ gchat (--version)
|
|
29
|
-
@hesed/gchat/0.
|
|
29
|
+
@hesed/gchat/0.4.0 linux-x64 node-v24.20.0
|
|
30
30
|
$ gchat --help [COMMAND]
|
|
31
31
|
USAGE
|
|
32
32
|
$ gchat COMMAND
|
|
@@ -38,6 +38,7 @@ USAGE
|
|
|
38
38
|
|
|
39
39
|
<!-- commands -->
|
|
40
40
|
* [`gchat gchat config add-token PROFILE SPACEID TOKEN`](#gchat-gchat-config-add-token-profile-spaceid-token)
|
|
41
|
+
* [`gchat gchat config add-user NAME USERID`](#gchat-gchat-config-add-user-name-userid)
|
|
41
42
|
* [`gchat gchat config set-key PROFILE KEY`](#gchat-gchat-config-set-key-profile-key)
|
|
42
43
|
* [`gchat gchat create-message SPACEID MESSAGE`](#gchat-gchat-create-message-spaceid-message)
|
|
43
44
|
* [`gchat gchat reply-message THREADNAME MESSAGE`](#gchat-gchat-reply-message-threadname-message)
|
|
@@ -62,7 +63,30 @@ EXAMPLES
|
|
|
62
63
|
$ gchat gchat config add-token default AAQAKA6hsFw your-space-token
|
|
63
64
|
```
|
|
64
65
|
|
|
65
|
-
_See code: [src/commands/gchat/config/add-token.ts](https://github.com/hesedcasa/gchat/blob/v0.
|
|
66
|
+
_See code: [src/commands/gchat/config/add-token.ts](https://github.com/hesedcasa/gchat/blob/v0.4.0/src/commands/gchat/config/add-token.ts)_
|
|
67
|
+
|
|
68
|
+
## `gchat gchat config add-user NAME USERID`
|
|
69
|
+
|
|
70
|
+
Add or update a user in the users list for tagging messages
|
|
71
|
+
|
|
72
|
+
```
|
|
73
|
+
USAGE
|
|
74
|
+
$ gchat gchat config add-user NAME USERID
|
|
75
|
+
|
|
76
|
+
ARGUMENTS
|
|
77
|
+
NAME Display name of the user, used with --tag
|
|
78
|
+
USERID Google Chat user ID (e.g. users/123456789)
|
|
79
|
+
|
|
80
|
+
DESCRIPTION
|
|
81
|
+
Add or update a user in the users list for tagging messages
|
|
82
|
+
|
|
83
|
+
EXAMPLES
|
|
84
|
+
$ gchat gchat config add-user "Jane Doe" users/123456789012345678901
|
|
85
|
+
|
|
86
|
+
$ gchat gchat config add-user "Jane Doe" 123456789012345678901
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
_See code: [src/commands/gchat/config/add-user.ts](https://github.com/hesedcasa/gchat/blob/v0.4.0/src/commands/gchat/config/add-user.ts)_
|
|
66
90
|
|
|
67
91
|
## `gchat gchat config set-key PROFILE KEY`
|
|
68
92
|
|
|
@@ -85,7 +109,7 @@ EXAMPLES
|
|
|
85
109
|
$ gchat gchat config set-key work your-work-api-key
|
|
86
110
|
```
|
|
87
111
|
|
|
88
|
-
_See code: [src/commands/gchat/config/set-key.ts](https://github.com/hesedcasa/gchat/blob/v0.
|
|
112
|
+
_See code: [src/commands/gchat/config/set-key.ts](https://github.com/hesedcasa/gchat/blob/v0.4.0/src/commands/gchat/config/set-key.ts)_
|
|
89
113
|
|
|
90
114
|
## `gchat gchat create-message SPACEID MESSAGE`
|
|
91
115
|
|
|
@@ -93,7 +117,7 @@ Send a message to a Google Chat space
|
|
|
93
117
|
|
|
94
118
|
```
|
|
95
119
|
USAGE
|
|
96
|
-
$ gchat gchat create-message SPACEID MESSAGE [-f] [-p <value>] [--toon]
|
|
120
|
+
$ gchat gchat create-message SPACEID MESSAGE [-f] [-p <value>] [-t <value>...] [--toon]
|
|
97
121
|
|
|
98
122
|
ARGUMENTS
|
|
99
123
|
SPACEID Google Chat space ID
|
|
@@ -102,6 +126,7 @@ ARGUMENTS
|
|
|
102
126
|
FLAGS
|
|
103
127
|
-f, --formatted Enable formatted text (bold, italic, links)
|
|
104
128
|
-p, --profile=<value> [default: default] Config profile to use
|
|
129
|
+
-t, --tag=<value>... User name or users/<id> to tag (repeatable)
|
|
105
130
|
--toon Format output as toon
|
|
106
131
|
|
|
107
132
|
DESCRIPTION
|
|
@@ -115,9 +140,11 @@ EXAMPLES
|
|
|
115
140
|
$ gchat gchat create-message AAQAKA6hsFw "*Bold message*" --formatted
|
|
116
141
|
|
|
117
142
|
$ gchat gchat create-message AAQAKA6hsFw "<https://example.com|Click here>" -f
|
|
143
|
+
|
|
144
|
+
$ gchat gchat create-message AAQAKA6hsFw "Deploy failed" --tag "Jane Doe"
|
|
118
145
|
```
|
|
119
146
|
|
|
120
|
-
_See code: [src/commands/gchat/create-message.ts](https://github.com/hesedcasa/gchat/blob/v0.
|
|
147
|
+
_See code: [src/commands/gchat/create-message.ts](https://github.com/hesedcasa/gchat/blob/v0.4.0/src/commands/gchat/create-message.ts)_
|
|
121
148
|
|
|
122
149
|
## `gchat gchat reply-message THREADNAME MESSAGE`
|
|
123
150
|
|
|
@@ -125,7 +152,7 @@ Reply to a message thread in Google Chat
|
|
|
125
152
|
|
|
126
153
|
```
|
|
127
154
|
USAGE
|
|
128
|
-
$ gchat gchat reply-message THREADNAME MESSAGE [-f] [-p <value>] [--toon]
|
|
155
|
+
$ gchat gchat reply-message THREADNAME MESSAGE [-f] [-p <value>] [-t <value>...] [--toon]
|
|
129
156
|
|
|
130
157
|
ARGUMENTS
|
|
131
158
|
THREADNAME Thread name (e.g. spaces/SPACE_ID/threads/THREAD_ID)
|
|
@@ -134,6 +161,7 @@ ARGUMENTS
|
|
|
134
161
|
FLAGS
|
|
135
162
|
-f, --formatted Enable formatted text (bold, italic, links)
|
|
136
163
|
-p, --profile=<value> [default: default] Config profile to use
|
|
164
|
+
-t, --tag=<value>... User name or users/<id> to tag (repeatable)
|
|
137
165
|
--toon Format output as toon
|
|
138
166
|
|
|
139
167
|
DESCRIPTION
|
|
@@ -145,7 +173,9 @@ EXAMPLES
|
|
|
145
173
|
$ gchat gchat reply-message spaces/AAQAKA6hsFw/threads/D1NI3W2B6vA "Reply here" --profile work
|
|
146
174
|
|
|
147
175
|
$ gchat gchat reply-message spaces/AAQAKA6hsFw/threads/D1NI3W2B6vA "*Bold reply*" --formatted
|
|
176
|
+
|
|
177
|
+
$ gchat gchat reply-message spaces/AAQAKA6hsFw/threads/D1NI3W2B6vA "Check this" --tag "Jane Doe"
|
|
148
178
|
```
|
|
149
179
|
|
|
150
|
-
_See code: [src/commands/gchat/reply-message.ts](https://github.com/hesedcasa/gchat/blob/v0.
|
|
180
|
+
_See code: [src/commands/gchat/reply-message.ts](https://github.com/hesedcasa/gchat/blob/v0.4.0/src/commands/gchat/reply-message.ts)_
|
|
151
181
|
<!-- commandsstop -->
|
|
@@ -10,7 +10,9 @@ export default class ConfigAddToken extends Command {
|
|
|
10
10
|
static examples = ['<%= config.bin %> <%= command.id %> default AAQAKA6hsFw your-space-token'];
|
|
11
11
|
async run() {
|
|
12
12
|
const { args } = await this.parse(ConfigAddToken);
|
|
13
|
-
const config = await readConfigOrEmpty(this.config.configDir);
|
|
13
|
+
const config = await readConfigOrEmpty(this.config.configDir, this.log.bind(this));
|
|
14
|
+
if (!config)
|
|
15
|
+
return;
|
|
14
16
|
const profile = config.profiles[args.profile] ?? { key: '', tokens: {} };
|
|
15
17
|
profile.tokens[args.spaceId] = args.token;
|
|
16
18
|
config.profiles[args.profile] = profile;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { Command } from '@oclif/core';
|
|
2
|
+
export default class ConfigAddUser extends Command {
|
|
3
|
+
static args: {
|
|
4
|
+
name: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
5
|
+
userId: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
|
|
6
|
+
};
|
|
7
|
+
static description: string;
|
|
8
|
+
static examples: string[];
|
|
9
|
+
run(): Promise<void>;
|
|
10
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Args, Command } from '@oclif/core';
|
|
2
|
+
import { addUser } from '../../../config.js';
|
|
3
|
+
export default class ConfigAddUser extends Command {
|
|
4
|
+
static args = {
|
|
5
|
+
name: Args.string({ description: 'Display name of the user, used with --tag', required: true }),
|
|
6
|
+
userId: Args.string({ description: 'Google Chat user ID (e.g. users/123456789)', required: true }),
|
|
7
|
+
};
|
|
8
|
+
static description = 'Add or update a user in the users list for tagging messages';
|
|
9
|
+
static examples = [
|
|
10
|
+
'<%= config.bin %> <%= command.id %> "Jane Doe" users/123456789012345678901',
|
|
11
|
+
'<%= config.bin %> <%= command.id %> "Jane Doe" 123456789012345678901',
|
|
12
|
+
];
|
|
13
|
+
async run() {
|
|
14
|
+
const { args } = await this.parse(ConfigAddUser);
|
|
15
|
+
const config = await addUser(this.config.configDir, args.name, args.userId, this.log.bind(this));
|
|
16
|
+
if (!config)
|
|
17
|
+
return;
|
|
18
|
+
this.log(`User '${args.name}' saved as ${config.users[args.name]} in the users list.`);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
@@ -14,7 +14,9 @@ export default class ConfigSetKey extends Command {
|
|
|
14
14
|
];
|
|
15
15
|
async run() {
|
|
16
16
|
const { args } = await this.parse(ConfigSetKey);
|
|
17
|
-
const config = await readConfigOrEmpty(this.config.configDir);
|
|
17
|
+
const config = await readConfigOrEmpty(this.config.configDir, this.log.bind(this));
|
|
18
|
+
if (!config)
|
|
19
|
+
return;
|
|
18
20
|
const profile = config.profiles[args.profile] ?? { key: '', tokens: {} };
|
|
19
21
|
profile.key = args.key;
|
|
20
22
|
config.profiles[args.profile] = profile;
|
|
@@ -9,6 +9,7 @@ export default class GChatCreateMessage extends Command {
|
|
|
9
9
|
static flags: {
|
|
10
10
|
formatted: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
11
11
|
profile: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
|
+
tag: import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
13
|
toon: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
13
14
|
};
|
|
14
15
|
run(): Promise<void>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Args, Command, Flags } from '@oclif/core';
|
|
2
|
-
import { DEFAULT_PROFILE, readConfig, resolveProfile } from '../../config.js';
|
|
2
|
+
import { DEFAULT_PROFILE, readConfig, resolveProfile, resolveTags } from '../../config.js';
|
|
3
3
|
import { formatAsToon } from '../../format.js';
|
|
4
4
|
import { clearClients, newMessage } from '../../gchat/gchat-client.js';
|
|
5
5
|
export default class GChatCreateMessage extends Command {
|
|
@@ -15,6 +15,7 @@ export default class GChatCreateMessage extends Command {
|
|
|
15
15
|
'<%= config.bin %> <%= command.id %> AAQAKA6hsFw "Hello work" --profile work',
|
|
16
16
|
'<%= config.bin %> <%= command.id %> AAQAKA6hsFw "*Bold message*" --formatted',
|
|
17
17
|
'<%= config.bin %> <%= command.id %> AAQAKA6hsFw "<https://example.com|Click here>" -f',
|
|
18
|
+
'<%= config.bin %> <%= command.id %> AAQAKA6hsFw "Deploy failed" --tag "Jane Doe"',
|
|
18
19
|
];
|
|
19
20
|
static flags = {
|
|
20
21
|
formatted: Flags.boolean({ char: 'f', description: 'Enable formatted text (bold, italic, links)', required: false }),
|
|
@@ -24,6 +25,12 @@ export default class GChatCreateMessage extends Command {
|
|
|
24
25
|
description: 'Config profile to use',
|
|
25
26
|
required: false,
|
|
26
27
|
}),
|
|
28
|
+
tag: Flags.string({
|
|
29
|
+
char: 't',
|
|
30
|
+
description: 'User name or users/<id> to tag (repeatable)',
|
|
31
|
+
multiple: true,
|
|
32
|
+
required: false,
|
|
33
|
+
}),
|
|
27
34
|
toon: Flags.boolean({ description: 'Format output as toon', required: false }),
|
|
28
35
|
};
|
|
29
36
|
async run() {
|
|
@@ -34,7 +41,14 @@ export default class GChatCreateMessage extends Command {
|
|
|
34
41
|
const auth = resolveProfile(config, flags.profile, this.log.bind(this));
|
|
35
42
|
if (!auth)
|
|
36
43
|
return;
|
|
37
|
-
const
|
|
44
|
+
const tags = resolveTags(config, flags.tag ?? [], this.log.bind(this));
|
|
45
|
+
if (!tags)
|
|
46
|
+
return;
|
|
47
|
+
let message = args.message.replaceAll(String.raw `\n`, '\n');
|
|
48
|
+
for (const tag of tags) {
|
|
49
|
+
message += `\n<${tag}>`;
|
|
50
|
+
}
|
|
51
|
+
const result = await newMessage(auth, args.spaceId, message, flags.formatted);
|
|
38
52
|
clearClients();
|
|
39
53
|
if (flags.toon) {
|
|
40
54
|
this.log(formatAsToon(result));
|
|
@@ -9,6 +9,7 @@ export default class GChatReplyMessage extends Command {
|
|
|
9
9
|
static flags: {
|
|
10
10
|
formatted: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
11
11
|
profile: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
|
+
tag: import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
|
|
12
13
|
toon: import("@oclif/core/interfaces").BooleanFlag<boolean>;
|
|
13
14
|
};
|
|
14
15
|
run(): Promise<void>;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Args, Command, Flags } from '@oclif/core';
|
|
2
|
-
import { DEFAULT_PROFILE, readConfig, resolveProfile } from '../../config.js';
|
|
2
|
+
import { DEFAULT_PROFILE, readConfig, resolveProfile, resolveTags } from '../../config.js';
|
|
3
3
|
import { formatAsToon } from '../../format.js';
|
|
4
4
|
import { clearClients, replyMessage } from '../../gchat/gchat-client.js';
|
|
5
5
|
export default class GChatReplyMessage extends Command {
|
|
@@ -17,6 +17,7 @@ export default class GChatReplyMessage extends Command {
|
|
|
17
17
|
'<%= config.bin %> <%= command.id %> spaces/AAQAKA6hsFw/threads/D1NI3W2B6vA "Reply here"',
|
|
18
18
|
'<%= config.bin %> <%= command.id %> spaces/AAQAKA6hsFw/threads/D1NI3W2B6vA "Reply here" --profile work',
|
|
19
19
|
'<%= config.bin %> <%= command.id %> spaces/AAQAKA6hsFw/threads/D1NI3W2B6vA "*Bold reply*" --formatted',
|
|
20
|
+
'<%= config.bin %> <%= command.id %> spaces/AAQAKA6hsFw/threads/D1NI3W2B6vA "Check this" --tag "Jane Doe"',
|
|
20
21
|
];
|
|
21
22
|
static flags = {
|
|
22
23
|
formatted: Flags.boolean({ char: 'f', description: 'Enable formatted text (bold, italic, links)', required: false }),
|
|
@@ -26,6 +27,12 @@ export default class GChatReplyMessage extends Command {
|
|
|
26
27
|
description: 'Config profile to use',
|
|
27
28
|
required: false,
|
|
28
29
|
}),
|
|
30
|
+
tag: Flags.string({
|
|
31
|
+
char: 't',
|
|
32
|
+
description: 'User name or users/<id> to tag (repeatable)',
|
|
33
|
+
multiple: true,
|
|
34
|
+
required: false,
|
|
35
|
+
}),
|
|
29
36
|
toon: Flags.boolean({ description: 'Format output as toon', required: false }),
|
|
30
37
|
};
|
|
31
38
|
async run() {
|
|
@@ -36,7 +43,14 @@ export default class GChatReplyMessage extends Command {
|
|
|
36
43
|
const auth = resolveProfile(config, flags.profile, this.log.bind(this));
|
|
37
44
|
if (!auth)
|
|
38
45
|
return;
|
|
39
|
-
const
|
|
46
|
+
const tags = resolveTags(config, flags.tag ?? [], this.log.bind(this));
|
|
47
|
+
if (!tags)
|
|
48
|
+
return;
|
|
49
|
+
let message = args.message.replaceAll(String.raw `\n`, '\n');
|
|
50
|
+
for (const tag of tags) {
|
|
51
|
+
message += `\n<${tag}>`;
|
|
52
|
+
}
|
|
53
|
+
const result = await replyMessage(auth, args.threadName, message, flags.formatted);
|
|
40
54
|
clearClients();
|
|
41
55
|
if (flags.toon) {
|
|
42
56
|
this.log(formatAsToon(result));
|
package/dist/config.d.ts
CHANGED
|
@@ -5,8 +5,11 @@ export type GChatAuth = {
|
|
|
5
5
|
};
|
|
6
6
|
export type GChatConfig = {
|
|
7
7
|
profiles: Record<string, GChatAuth>;
|
|
8
|
+
users: Record<string, string>;
|
|
8
9
|
};
|
|
9
|
-
export declare function readConfigOrEmpty(configDir: string): Promise<GChatConfig>;
|
|
10
|
+
export declare function readConfigOrEmpty(configDir: string, log: (msg: string) => void): Promise<GChatConfig | null>;
|
|
10
11
|
export declare function writeConfig(configDir: string, config: GChatConfig): Promise<void>;
|
|
11
12
|
export declare function readConfig(configDir: string, log: (msg: string) => void): Promise<GChatConfig | null>;
|
|
12
13
|
export declare function resolveProfile(config: GChatConfig, profile: string, log: (msg: string) => void): GChatAuth | null;
|
|
14
|
+
export declare function addUser(configDir: string, name: string, userId: string, log: (msg: string) => void): Promise<GChatConfig | null>;
|
|
15
|
+
export declare function resolveTags(config: GChatConfig, names: string[], log: (msg: string) => void): null | string[];
|
package/dist/config.js
CHANGED
|
@@ -12,26 +12,40 @@ function parseProfiles(raw) {
|
|
|
12
12
|
// Legacy single-profile config: promote top-level key/tokens to the default profile.
|
|
13
13
|
return { [DEFAULT_PROFILE]: { key: raw.key ?? '', tokens: raw.tokens ?? {} } };
|
|
14
14
|
}
|
|
15
|
-
|
|
15
|
+
function parseUsers(raw) {
|
|
16
|
+
if (raw.users && typeof raw.users === 'object' && !Array.isArray(raw.users)) {
|
|
17
|
+
return raw.users;
|
|
18
|
+
}
|
|
19
|
+
return {};
|
|
20
|
+
}
|
|
21
|
+
export async function readConfigOrEmpty(configDir, log) {
|
|
16
22
|
const configPath = path.join(configDir, 'gchat-config.json');
|
|
17
23
|
try {
|
|
18
24
|
const raw = JSON.parse(await readFile(configPath, 'utf8'));
|
|
19
|
-
return { profiles: parseProfiles(raw) };
|
|
25
|
+
return { profiles: parseProfiles(raw), users: parseUsers(raw) };
|
|
20
26
|
}
|
|
21
|
-
catch {
|
|
22
|
-
|
|
27
|
+
catch (error) {
|
|
28
|
+
// A missing file means the user has not configured anything yet — start empty.
|
|
29
|
+
// Any other failure (malformed JSON, unreadable file) must not be treated as
|
|
30
|
+
// an empty config, or the next write would silently destroy the existing one.
|
|
31
|
+
if (error instanceof Error && error.code === 'ENOENT') {
|
|
32
|
+
return { profiles: {}, users: {} };
|
|
33
|
+
}
|
|
34
|
+
log(`Error: Could not read config from ${configPath}`);
|
|
35
|
+
log('Fix or remove the file and retry — nothing was overwritten.');
|
|
36
|
+
return null;
|
|
23
37
|
}
|
|
24
38
|
}
|
|
25
39
|
export async function writeConfig(configDir, config) {
|
|
26
40
|
const configPath = path.join(configDir, 'gchat-config.json');
|
|
27
41
|
await mkdir(configDir, { recursive: true });
|
|
28
|
-
await writeFile(configPath, JSON.stringify({ profiles: config.profiles }, null, 2));
|
|
42
|
+
await writeFile(configPath, JSON.stringify({ profiles: config.profiles, users: config.users }, null, 2));
|
|
29
43
|
}
|
|
30
44
|
export async function readConfig(configDir, log) {
|
|
31
45
|
const configPath = path.join(configDir, 'gchat-config.json');
|
|
32
46
|
try {
|
|
33
47
|
const raw = JSON.parse(await readFile(configPath, 'utf8'));
|
|
34
|
-
return { profiles: parseProfiles(raw) };
|
|
48
|
+
return { profiles: parseProfiles(raw), users: parseUsers(raw) };
|
|
35
49
|
}
|
|
36
50
|
catch {
|
|
37
51
|
log(`Error: Could not read config from ${configPath}`);
|
|
@@ -47,3 +61,57 @@ export function resolveProfile(config, profile, log) {
|
|
|
47
61
|
}
|
|
48
62
|
return auth;
|
|
49
63
|
}
|
|
64
|
+
const USER_PREFIX = 'users/';
|
|
65
|
+
function isUserId(value) {
|
|
66
|
+
if (!value.startsWith(USER_PREFIX))
|
|
67
|
+
return false;
|
|
68
|
+
const id = value.slice(USER_PREFIX.length);
|
|
69
|
+
return id.length > 0 && [...id].every((char) => char.trim().length > 0);
|
|
70
|
+
}
|
|
71
|
+
export async function addUser(configDir, name, userId, log) {
|
|
72
|
+
const config = await readConfigOrEmpty(configDir, log);
|
|
73
|
+
if (!config)
|
|
74
|
+
return null;
|
|
75
|
+
const normalized = userId.startsWith(USER_PREFIX) ? userId : `${USER_PREFIX}${userId}`;
|
|
76
|
+
if (!isUserId(normalized)) {
|
|
77
|
+
log(`Error: Invalid user ID '${userId}'. Expected a 'users/<id>' value with a non-empty, whitespace-free ID.`);
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
config.users[name] = normalized;
|
|
81
|
+
await writeConfig(configDir, config);
|
|
82
|
+
return config;
|
|
83
|
+
}
|
|
84
|
+
export function resolveTags(config, names, log) {
|
|
85
|
+
const resolved = [];
|
|
86
|
+
const invalid = [];
|
|
87
|
+
const unknown = [];
|
|
88
|
+
for (const name of names) {
|
|
89
|
+
// Own-property lookup only — inherited names like 'toString' must count as unknown.
|
|
90
|
+
const saved = Object.hasOwn(config.users, name) ? config.users[name] : undefined;
|
|
91
|
+
if (saved === undefined) {
|
|
92
|
+
if (isUserId(name)) {
|
|
93
|
+
resolved.push(name);
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
unknown.push(name);
|
|
97
|
+
}
|
|
98
|
+
continue;
|
|
99
|
+
}
|
|
100
|
+
if (isUserId(saved)) {
|
|
101
|
+
resolved.push(saved);
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
invalid.push(name);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
if (invalid.length === 0 && unknown.length === 0)
|
|
108
|
+
return resolved;
|
|
109
|
+
for (const name of invalid) {
|
|
110
|
+
log(`Error: Saved user '${name}' has an invalid ID: '${config.users[name]}'.`);
|
|
111
|
+
}
|
|
112
|
+
for (const name of unknown) {
|
|
113
|
+
log(`Error: User '${name}' not found in the users list.`);
|
|
114
|
+
}
|
|
115
|
+
log("Run 'gchat config add-user <name> <userId>' to add or update a user.");
|
|
116
|
+
return null;
|
|
117
|
+
}
|
package/oclif.manifest.json
CHANGED
|
@@ -19,7 +19,8 @@
|
|
|
19
19
|
"<%= config.bin %> <%= command.id %> AAQAKA6hsFw \"Hello team\"",
|
|
20
20
|
"<%= config.bin %> <%= command.id %> AAQAKA6hsFw \"Hello work\" --profile work",
|
|
21
21
|
"<%= config.bin %> <%= command.id %> AAQAKA6hsFw \"*Bold message*\" --formatted",
|
|
22
|
-
"<%= config.bin %> <%= command.id %> AAQAKA6hsFw \"<https://example.com|Click here>\" -f"
|
|
22
|
+
"<%= config.bin %> <%= command.id %> AAQAKA6hsFw \"<https://example.com|Click here>\" -f",
|
|
23
|
+
"<%= config.bin %> <%= command.id %> AAQAKA6hsFw \"Deploy failed\" --tag \"Jane Doe\""
|
|
23
24
|
],
|
|
24
25
|
"flags": {
|
|
25
26
|
"formatted": {
|
|
@@ -40,6 +41,15 @@
|
|
|
40
41
|
"multiple": false,
|
|
41
42
|
"type": "option"
|
|
42
43
|
},
|
|
44
|
+
"tag": {
|
|
45
|
+
"char": "t",
|
|
46
|
+
"description": "User name or users/<id> to tag (repeatable)",
|
|
47
|
+
"name": "tag",
|
|
48
|
+
"required": false,
|
|
49
|
+
"hasDynamicHelp": false,
|
|
50
|
+
"multiple": true,
|
|
51
|
+
"type": "option"
|
|
52
|
+
},
|
|
43
53
|
"toon": {
|
|
44
54
|
"description": "Format output as toon",
|
|
45
55
|
"name": "toon",
|
|
@@ -82,7 +92,8 @@
|
|
|
82
92
|
"examples": [
|
|
83
93
|
"<%= config.bin %> <%= command.id %> spaces/AAQAKA6hsFw/threads/D1NI3W2B6vA \"Reply here\"",
|
|
84
94
|
"<%= config.bin %> <%= command.id %> spaces/AAQAKA6hsFw/threads/D1NI3W2B6vA \"Reply here\" --profile work",
|
|
85
|
-
"<%= config.bin %> <%= command.id %> spaces/AAQAKA6hsFw/threads/D1NI3W2B6vA \"*Bold reply*\" --formatted"
|
|
95
|
+
"<%= config.bin %> <%= command.id %> spaces/AAQAKA6hsFw/threads/D1NI3W2B6vA \"*Bold reply*\" --formatted",
|
|
96
|
+
"<%= config.bin %> <%= command.id %> spaces/AAQAKA6hsFw/threads/D1NI3W2B6vA \"Check this\" --tag \"Jane Doe\""
|
|
86
97
|
],
|
|
87
98
|
"flags": {
|
|
88
99
|
"formatted": {
|
|
@@ -103,6 +114,15 @@
|
|
|
103
114
|
"multiple": false,
|
|
104
115
|
"type": "option"
|
|
105
116
|
},
|
|
117
|
+
"tag": {
|
|
118
|
+
"char": "t",
|
|
119
|
+
"description": "User name or users/<id> to tag (repeatable)",
|
|
120
|
+
"name": "tag",
|
|
121
|
+
"required": false,
|
|
122
|
+
"hasDynamicHelp": false,
|
|
123
|
+
"multiple": true,
|
|
124
|
+
"type": "option"
|
|
125
|
+
},
|
|
106
126
|
"toon": {
|
|
107
127
|
"description": "Format output as toon",
|
|
108
128
|
"name": "toon",
|
|
@@ -168,6 +188,43 @@
|
|
|
168
188
|
"add-token.js"
|
|
169
189
|
]
|
|
170
190
|
},
|
|
191
|
+
"gchat:config:add-user": {
|
|
192
|
+
"aliases": [],
|
|
193
|
+
"args": {
|
|
194
|
+
"name": {
|
|
195
|
+
"description": "Display name of the user, used with --tag",
|
|
196
|
+
"name": "name",
|
|
197
|
+
"required": true
|
|
198
|
+
},
|
|
199
|
+
"userId": {
|
|
200
|
+
"description": "Google Chat user ID (e.g. users/123456789)",
|
|
201
|
+
"name": "userId",
|
|
202
|
+
"required": true
|
|
203
|
+
}
|
|
204
|
+
},
|
|
205
|
+
"description": "Add or update a user in the users list for tagging messages",
|
|
206
|
+
"examples": [
|
|
207
|
+
"<%= config.bin %> <%= command.id %> \"Jane Doe\" users/123456789012345678901",
|
|
208
|
+
"<%= config.bin %> <%= command.id %> \"Jane Doe\" 123456789012345678901"
|
|
209
|
+
],
|
|
210
|
+
"flags": {},
|
|
211
|
+
"hasDynamicHelp": false,
|
|
212
|
+
"hiddenAliases": [],
|
|
213
|
+
"id": "gchat:config:add-user",
|
|
214
|
+
"pluginAlias": "@hesed/gchat",
|
|
215
|
+
"pluginName": "@hesed/gchat",
|
|
216
|
+
"pluginType": "core",
|
|
217
|
+
"strict": true,
|
|
218
|
+
"enableJsonFlag": false,
|
|
219
|
+
"isESM": true,
|
|
220
|
+
"relativePath": [
|
|
221
|
+
"dist",
|
|
222
|
+
"commands",
|
|
223
|
+
"gchat",
|
|
224
|
+
"config",
|
|
225
|
+
"add-user.js"
|
|
226
|
+
]
|
|
227
|
+
},
|
|
171
228
|
"gchat:config:set-key": {
|
|
172
229
|
"aliases": [],
|
|
173
230
|
"args": {
|
|
@@ -206,5 +263,5 @@
|
|
|
206
263
|
]
|
|
207
264
|
}
|
|
208
265
|
},
|
|
209
|
-
"version": "0.
|
|
266
|
+
"version": "0.4.0"
|
|
210
267
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hesed/gchat",
|
|
3
3
|
"description": "CLI for Google Chat API interaction",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.4.0",
|
|
5
5
|
"author": "Hesed",
|
|
6
6
|
"bin": {
|
|
7
7
|
"gchat": "./bin/run.js"
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"@types/mocha": "^10",
|
|
23
23
|
"@types/node": "^26",
|
|
24
24
|
"@types/sinon": "^22.0.0",
|
|
25
|
-
"c8": "^
|
|
25
|
+
"c8": "^12.0.0",
|
|
26
26
|
"chai": "^6",
|
|
27
27
|
"eslint": "^10.8.1",
|
|
28
28
|
"eslint-config-oclif": "^7.1.5",
|