Skip to content

Commit e9fceb6

Browse files
committed
Merge remote-tracking branch 'upstream/master'
2 parents 36c9c51 + 1647a24 commit e9fceb6

File tree

13 files changed

+2161
-83
lines changed

13 files changed

+2161
-83
lines changed

assets/images/icons/vue.png

6.32 KB
Loading

data/api.hjson

+1,398
Large diffs are not rendered by default.

package-lock.json

+10
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+2
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
3131
"eslint-plugin-prettier": "^3.1.0",
3232
"eslint-plugin-vue": "^5.2.3",
3333
"esm": "^3.2.25",
34+
"fuse.js": "^3.4.6",
35+
"hjson": "^3.1.2",
3436
"prettier": "^1.18.2"
3537
},
3638
"devDependencies": {

src/commands/documentation/api.js

+201
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
import { Command } from 'discord.js-commando'
2+
import { getAPI, findAPI, APINotFoundError } from '../../services/api'
3+
import { RichEmbed } from 'discord.js'
4+
import { EMPTY_MESSAGE } from '../../utils/constants'
5+
import { inlineCode, blockCode } from '../../utils/string'
6+
import { cleanupInvocation, cleanupErrorResponse } from '../../utils/messages'
7+
import {
8+
DEFAULT_EMBED_COLOUR,
9+
respondWithPaginatedEmbed,
10+
} from '../../utils/embed'
11+
12+
module.exports = class DocumentationAPICommand extends Command {
13+
constructor(client) {
14+
super(client, {
15+
args: [
16+
{
17+
key: 'lookup',
18+
type: 'string',
19+
prompt: 'what do you want to look up in the API docs?',
20+
},
21+
],
22+
name: 'api',
23+
examples: [
24+
inlineCode('!api errorHandler'),
25+
inlineCode('!api Vue.observable'),
26+
inlineCode('!api observable'),
27+
inlineCode('!api computed'),
28+
inlineCode('!api template'),
29+
inlineCode('!api mounted'),
30+
inlineCode('!api directives'),
31+
inlineCode('!api mixins'),
32+
inlineCode('!api delimiters'),
33+
inlineCode('!api vm.$props'),
34+
inlineCode('!api vm.$watch'),
35+
inlineCode('!api vm.$on'),
36+
inlineCode('!api vm.$mount'),
37+
inlineCode('!api $props'),
38+
inlineCode('!api $watch'),
39+
inlineCode('!api $on'),
40+
inlineCode('!api $mount'),
41+
inlineCode('!api v-html'),
42+
inlineCode('!api ref'),
43+
inlineCode('!api keep-alive'),
44+
],
45+
group: 'documentation',
46+
aliases: ['a'],
47+
guildOnly: false,
48+
memberName: 'api',
49+
description: 'Look something up in the API docs.',
50+
})
51+
}
52+
53+
hasPermission() {
54+
return true
55+
}
56+
57+
async run(msg, args) {
58+
const { lookup } = args
59+
60+
let embed
61+
62+
try {
63+
// Try to find an exact match (or alias).
64+
let api = getAPI(lookup)
65+
66+
if (api) {
67+
embed = this.buildResponseEmbed(msg, api)
68+
} else {
69+
// Attempt a fuzzy search.
70+
api = findAPI(lookup)
71+
72+
if (api) {
73+
if (api.length > 1) {
74+
return respondWithPaginatedEmbed(
75+
msg,
76+
this.buildDisambiguationEmbed(msg, lookup, api),
77+
api.map(item => this.buildResponseEmbed(msg, item))
78+
)
79+
} else if (api.length === 1) {
80+
embed = this.buildResponseEmbed(msg, api[0])
81+
}
82+
}
83+
}
84+
85+
if (!api || api.length === 0) {
86+
throw new APINotFoundError(
87+
"Sorry, I couldn't find any matches for your query in the API docs."
88+
)
89+
}
90+
91+
await msg.channel.send(EMPTY_MESSAGE, { embed })
92+
cleanupInvocation(msg)
93+
} catch (error) {
94+
console.error(error)
95+
96+
const reply = await msg.channel.send(EMPTY_MESSAGE, {
97+
embed: this.buildErrorEmbed(msg, lookup, error),
98+
})
99+
100+
cleanupInvocation(msg)
101+
cleanupErrorResponse(reply)
102+
}
103+
}
104+
105+
buildErrorEmbed(msg, lookup, error) {
106+
return new RichEmbed()
107+
.setTitle(`API Lookup: ${inlineCode(lookup)}`)
108+
.setDescription(error.message)
109+
.setAuthor(
110+
(msg.member ? msg.member.displayName : msg.author.username) +
111+
' requested:',
112+
msg.author.avatarURL
113+
)
114+
.setColor('RED')
115+
}
116+
117+
buildDisambiguationEmbed(msg, lookup, results) {
118+
return new RichEmbed()
119+
.setTitle(`API Lookup: ${inlineCode(lookup)}`)
120+
.setDescription(
121+
"Sorry, I couldn't find an exact match for your query in the API docs."
122+
)
123+
.setThumbnail('attachment://vue.png')
124+
.attachFile({
125+
attachment: 'assets/images/icons/vue.png',
126+
name: 'vue.png',
127+
})
128+
.addField(
129+
'Perhaps you meant one of these:',
130+
results.map(result => inlineCode(result.id)).join(', ')
131+
)
132+
.setAuthor(
133+
(msg.member ? msg.member.displayName : msg.author.username) +
134+
' requested:',
135+
msg.author.avatarURL
136+
)
137+
.setColor('BLUE')
138+
}
139+
140+
buildResponseEmbed(msg, api) {
141+
const embed = new RichEmbed()
142+
.setTitle(api.title)
143+
.setAuthor(
144+
(msg.member ? msg.member.displayName : msg.author.username) +
145+
' requested:',
146+
msg.author.avatarURL
147+
)
148+
.setURL(api.link)
149+
.setFooter(`Category: ${api.category}`)
150+
.setThumbnail('attachment://vue.png')
151+
.attachFile({
152+
attachment: 'assets/images/icons/vue.png',
153+
name: 'vue.png',
154+
})
155+
156+
if (api.description) {
157+
embed.setDescription(api.description)
158+
}
159+
160+
if (api.status) {
161+
embed.addField('Status', api.status)
162+
163+
if (api.status === 'deprecated') {
164+
embed.setColor('ORANGE')
165+
} else if (api.status === 'removed') {
166+
embed.setColor('RED')
167+
}
168+
} else {
169+
embed.setColor(DEFAULT_EMBED_COLOUR)
170+
}
171+
172+
if (api.type) {
173+
embed.addField('Type', inlineCode(api.type), true)
174+
}
175+
176+
if (api.default) {
177+
embed.addField('Default', inlineCode(api.default), true)
178+
}
179+
180+
if (api.version) {
181+
embed.addField('Since', api.version, true)
182+
}
183+
184+
if (api.arguments) {
185+
embed.addField(
186+
'Arguments',
187+
api.arguments.map(arg => inlineCode(arg)).join('\n')
188+
)
189+
}
190+
191+
if (api.usage && api.usage.lang && api.usage.code) {
192+
embed.addField('Usage', blockCode(api.usage.code, api.usage.lang))
193+
}
194+
195+
if (api.see && api.see.length) {
196+
embed.addField('See Also', api.see.join('\n'))
197+
}
198+
199+
return embed
200+
}
201+
}

src/commands/miscellaneous/roles.js

+108
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
import { Command } from 'discord.js-commando'
2+
import { RichEmbed } from 'discord.js'
3+
import { ROLES } from '../../utils/constants'
4+
import { tryDelete } from '../../utils/messages'
5+
6+
const ROLE_INFORMATION = [
7+
{
8+
title: 'Core Team',
9+
content: `The <@&${ROLES.CORE_TEAM}> are the Vue.js developers. They mostly frequent the #vue2-internals and #vue3-discussions channels.`,
10+
},
11+
{
12+
title: 'Moderators',
13+
content: `The <@&${ROLES.MODERATORS}> are the keepers of peace and order. Feel free to ping (or preferably DM) them if there is any issue which requires their attention.`,
14+
},
15+
{
16+
title: 'MVPs',
17+
content: `The <@&${ROLES.MVPS}> are people who've proven to be helpful on the server and may assist you with your issue, if they're available. They generally frequent #code-help and #code-help-too.`,
18+
},
19+
{
20+
title: 'Library Maintainers',
21+
content: `The <@&${ROLES.LIBRARY_MAINTAINERS}> are people who are maintainers or contributors of popular/important Vue.js libraries, frameworks and tools.`,
22+
},
23+
{
24+
title: 'Community Leaders',
25+
content: `The <@&${ROLES.COMMUNITY_LEADERS}> are in charge of organising and running things like meetups, events and conferences, or notable community projects and resources.`,
26+
},
27+
{
28+
title: 'Nitro Booster',
29+
content: `The <@&${ROLES.NITRO_BOOSTERS}> role consists of people who have boosted the server with their Discord Nitro membership (thanks)!`,
30+
},
31+
]
32+
33+
module.exports = class MiscCodeCommand extends Command {
34+
constructor(client) {
35+
super(client, {
36+
args: [
37+
{
38+
key: 'role',
39+
type: 'role',
40+
prompt:
41+
'info. about a specific role only (optional), or all roles (default)?',
42+
error:
43+
'invalid role provided - please respond with a role (example: `Moderators`).',
44+
default: 'all',
45+
},
46+
],
47+
name: 'roles',
48+
group: 'miscellaneous',
49+
aliases: ['r', 'role'],
50+
guildOnly: true,
51+
memberName: 'roles',
52+
description: 'Explains the various roles on the server',
53+
examples: [
54+
'`!roles` - Show info. for all roles.',
55+
'`!role Moderators` - Show info. for a specific role.',
56+
'`!role Mod` - Partial matches are supported.',
57+
],
58+
})
59+
}
60+
61+
hasPermission() {
62+
return true
63+
}
64+
65+
async run(msg, args) {
66+
const { role } = args
67+
68+
const embedMessage = new RichEmbed()
69+
.setColor('#42b883')
70+
.setAuthor(
71+
msg.member ? msg.member.displayName : msg.author.username,
72+
msg.author.avatarURL
73+
)
74+
75+
if (role === 'all') {
76+
for (const roleInfo of ROLE_INFORMATION) {
77+
embedMessage.addField(roleInfo.title, roleInfo.content)
78+
}
79+
80+
embedMessage
81+
.setTitle('Vue Land Roles')
82+
.setDescription(
83+
'**NOTE:** Please do not ping any of these roles (except Moderators).'
84+
)
85+
} else {
86+
const matchedRole = ROLE_INFORMATION.find(
87+
roleInfo => roleInfo.title === role.name
88+
)
89+
90+
if (matchedRole) {
91+
embedMessage
92+
.setColor(role.color)
93+
.setTitle(`Vue Land Roles - ${matchedRole.title}`)
94+
.setDescription(matchedRole.content)
95+
} else {
96+
embedMessage
97+
.setColor('ORANGE')
98+
.setTitle(`Vue Land Roles`)
99+
.setDescription(
100+
`Sorry but we we don't have any information about the ${role} role.`
101+
)
102+
}
103+
}
104+
105+
await msg.channel.send(embedMessage)
106+
tryDelete(msg)
107+
}
108+
}

0 commit comments

Comments
 (0)