prismarine-chat 1.3.2 → 1.5.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/HISTORY.md CHANGED
@@ -1,5 +1,21 @@
1
1
  ## History
2
2
 
3
+ ### 1.5.0
4
+
5
+ * allow ChatMessage constructor to accept strings with legacy color codes (§) be converted to new json
6
+
7
+ ### 1.4.1
8
+ * Remake ChatMessage#clone (@u9g)
9
+
10
+ ### 1.4.0
11
+ * Add a example (@u9g)
12
+ * Add hex color code support (@U5B)
13
+ * use rest arg to allow many withs or extras (@u9g)
14
+ * Add missing json attribute to typescript defintions (@Paulomart & @u9g)
15
+
16
+ ### 1.3.3
17
+ * fix typings
18
+
3
19
  ### 1.3.2
4
20
  * Properly export loader function and export ChatMessage & MessageBuilder as types
5
21
 
package/MessageBuilder.js CHANGED
@@ -144,16 +144,29 @@ function loader (version) {
144
144
  /**
145
145
  * appended to the end of this message object with the existing formatting.
146
146
  * formatting can be overrode in child messagebuilder
147
- * @param {MessageBuilder|string} val
147
+ * @param {Array<MessageBuilder | string>} ...args
148
148
  * @returns
149
149
  */
150
- addExtra (val) { this.extra.push(typeof val === 'string' ? val : val.toJSON()); return this }
150
+ addExtra (...args) {
151
+ for (const v of args) {
152
+ const value = typeof v === 'string' ? v : v.toJSON()
153
+ this.extra.push(value)
154
+ }
155
+ return this
156
+ }
157
+
151
158
  /**
152
159
  * requires .translate to be set for this to be used
153
- * @param {MessageBuilder|string} val
160
+ * @param {Array<MessageBuilder | string>} ...args
154
161
  * @returns
155
162
  */
156
- addWith (val) { this.with.push(typeof val === 'string' ? val : val.toJSON()); return this }
163
+ addWith (...args) {
164
+ for (const v of args) {
165
+ const value = typeof v === 'string' ? v : v.toJSON()
166
+ this.with.push(value)
167
+ }
168
+ return this
169
+ }
157
170
 
158
171
  resetFormatting () {
159
172
  this.setBold(false)
package/README.md CHANGED
@@ -90,8 +90,8 @@ Returns a prismarine-chat representation of the message recieved from the 'chat'
90
90
  #### setScore (name: string, objective: string) : this
91
91
  #### setClickEvent (action: string, value: object) : this
92
92
  #### setHoverEvent (action: string, data: object, type?: 'contents'|'value') : this
93
- #### addExtra (val: MessageBuilder | string) : this
94
- #### addWith (val: MessageBuilder | string) : this
93
+ #### addExtra (...val: MessageBuilder | string) : this
94
+ #### addWith (...val: MessageBuilder | string) : this
95
95
  #### resetFormatting () : void
96
96
  sets every one of the formatting options to false and sets text to `reset` type
97
97
 
package/index.d.ts CHANGED
@@ -1,11 +1,8 @@
1
1
  declare const loader: (mcVersion: string) => typeof ChatMessage
2
2
 
3
- export = loader
3
+ export default loader
4
4
 
5
- export type ChatMessage = ChatMessage
6
- export type MessageBuilder = MessageBuilder
7
-
8
- declare class ChatMessage {
5
+ export declare class ChatMessage {
9
6
  // for export
10
7
  static MessageBuilder: typeof MessageBuilder
11
8
 
@@ -17,6 +14,8 @@ declare class ChatMessage {
17
14
  displayWarning?: boolean
18
15
  )
19
16
 
17
+ public readonly json: any
18
+
20
19
  /**
21
20
  * Append one or more ChatMessages
22
21
  */
@@ -26,6 +25,13 @@ declare class ChatMessage {
26
25
  * Returns a clone of the ChatMessage
27
26
  */
28
27
  clone(): ChatMessage
28
+
29
+ /**
30
+ * Optional info with no guarantee for form or shape.
31
+ */
32
+ extra?: Array<ChatMessage>
33
+
34
+ translate?: string
29
35
 
30
36
  /**
31
37
  * Flattens the message into plain-text, without style.
@@ -66,7 +72,7 @@ declare class ChatMessage {
66
72
 
67
73
  type Language = { [key: string]: string }
68
74
 
69
- declare class MessageBuilder {
75
+ export declare class MessageBuilder {
70
76
  with: string[]
71
77
  extra: string[]
72
78
  bold?: boolean
@@ -167,19 +173,21 @@ declare class MessageBuilder {
167
173
  * appended to the end of this message object with the existing formatting.
168
174
  * formatting can be overridden in child messagebuilder
169
175
  */
170
- addExtra(val: MessageBuilder | string): this
176
+ addExtra(...args: Array<MessageBuilder | string>): this
171
177
 
172
178
  /**
173
179
  * requires .translate to be set for this to be used
174
180
  */
175
- addWith(val: MessageBuilder | string): this
181
+ addWith(...args: Array<MessageBuilder | string>): this
176
182
 
177
183
  resetFormatting(): void
178
184
 
179
185
  toJSON(): object
180
186
 
181
187
  toString(): string
182
-
188
+ /**
189
+ * fromString('&aHello').toJSON() => { text: 'Hello', color: 'aqua' }
190
+ */
183
191
  static fromString(
184
192
  str: string,
185
193
  args?: { colorSeparator?: string }
package/index.js CHANGED
@@ -5,361 +5,378 @@ module.exports = loader
5
5
 
6
6
  function loader (mcVersion) {
7
7
  const mcData = require('minecraft-data')(mcVersion)
8
- defaultLang = mcData.language
8
+ const defaultLang = mcData.language
9
9
  const { MessageBuilder } = require('./MessageBuilder')(mcVersion)
10
- ChatMessage.MessageBuilder = MessageBuilder
11
- return ChatMessage
12
- }
13
-
14
- let defaultLang
15
-
16
- /**
17
- * ChatMessage Constructor
18
- * @param {String|Object|Number} message content of ChatMessage
19
- */
20
- class ChatMessage {
21
- constructor (message, displayWarning = false) {
22
- if (typeof message === 'string' || typeof message === 'number') {
23
- this.json = { text: message }
24
- } else if (typeof message === 'object' && !Array.isArray(message)) {
25
- this.json = message
26
- } else if (typeof message === 'object') {
27
- this.json = { extra: message }
28
- } else {
29
- throw new Error('Expected String or Object for Message argument')
30
- }
31
- this.parse(displayWarning)
32
- }
33
10
 
34
11
  /**
35
- * Parses the this.json property to decorate the properties of the ChatMessage.
36
- * Called by the Constructor
37
- * @return {void}
12
+ * ChatMessage Constructor
13
+ * @param {String|Object|Number} message content of ChatMessage
38
14
  */
39
- parse (displayWarning = false) {
40
- const json = this.json
41
- // Message scope for callback functions
42
- // There is EITHER, a text property or a translate property
43
- // If there is no translate property, there is no with property
44
- // HOWEVER! If there is a translate property, there may not be a with property
45
- if (typeof json.text === 'string' || typeof json.text === 'number') {
46
- this.text = json.text
47
- } else if (typeof json.translate === 'string') {
48
- this.translate = json.translate
49
- if (typeof json.with === 'object') {
50
- if (!Array.isArray(json.with)) {
51
- throw new Error('Expected with property to be an Array in ChatMessage')
15
+ class ChatMessage {
16
+ constructor (message, displayWarning = false) {
17
+ if (typeof message === 'string') {
18
+ if (message === '') {
19
+ this.json = { text: '' }
20
+ } else {
21
+ this.json = MessageBuilder.fromString(message, { colorSeparator: '§' })
52
22
  }
53
- this.with = json.with.map(entry => new ChatMessage(entry))
54
- }
55
- }
56
- // Parse extra property
57
- // Extras are appended to the initial text
58
- if (typeof json.extra === 'object') {
59
- if (!Array.isArray(json.extra)) {
60
- throw new Error('Expected extra property to be an Array in ChatMessage')
23
+ } else if (typeof message === 'number') {
24
+ this.json = { text: message }
25
+ } else if (typeof message === 'object' && Array.isArray(message)) {
26
+ this.json = { extra: message }
27
+ } else if (typeof message === 'object') {
28
+ this.json = message
29
+ } else {
30
+ throw new Error('Expected String or Object for Message argument')
61
31
  }
62
- this.extra = json.extra.map(entry => new ChatMessage(entry))
32
+ this.parse(displayWarning)
63
33
  }
64
- // Text modifiers
65
- this.bold = json.bold
66
- this.italic = json.italic
67
- this.underlined = json.underlined
68
- this.strikethrough = json.strikethrough
69
- this.obfuscated = json.obfuscated
70
34
 
71
- // Supported constants @ 2014-04-21
72
- const supportedColors = [
73
- 'black',
74
- 'dark_blue',
75
- 'dark_green',
76
- 'dark_aqua',
77
- 'dark_red',
78
- 'dark_purple',
79
- 'gold',
80
- 'gray',
81
- 'dark_gray',
82
- 'blue',
83
- 'green',
84
- 'aqua',
85
- 'red',
86
- 'light_purple',
87
- 'yellow',
88
- 'white',
89
- 'obfuscated',
90
- 'bold',
91
- 'strikethrough',
92
- 'underlined',
93
- 'italic',
94
- 'reset'
95
- ]
96
- const supportedClick = [
97
- 'open_url',
98
- 'open_file',
99
- 'run_command',
100
- 'suggest_command'
101
- ]
102
- const supportedHover = [
103
- 'show_text',
104
- 'show_achievement',
105
- 'show_item',
106
- 'show_entity'
107
- ]
35
+ /**
36
+ * Parses the this.json property to decorate the properties of the ChatMessage.
37
+ * Called by the Constructor
38
+ * @return {void}
39
+ */
40
+ parse (displayWarning = false) {
41
+ const json = this.json
42
+ // Message scope for callback functions
43
+ // There is EITHER, a text property or a translate property
44
+ // If there is no translate property, there is no with property
45
+ // HOWEVER! If there is a translate property, there may not be a with property
46
+ if (typeof json.text === 'string' || typeof json.text === 'number') {
47
+ this.text = json.text
48
+ } else if (typeof json.translate === 'string') {
49
+ this.translate = json.translate
50
+ if (typeof json.with === 'object') {
51
+ if (!Array.isArray(json.with)) {
52
+ throw new Error('Expected with property to be an Array in ChatMessage')
53
+ }
54
+ this.with = json.with.map(entry => new ChatMessage(entry))
55
+ }
56
+ }
57
+ // Parse extra property
58
+ // Extras are appended to the initial text
59
+ if (typeof json.extra === 'object') {
60
+ if (!Array.isArray(json.extra)) {
61
+ throw new Error('Expected extra property to be an Array in ChatMessage')
62
+ }
63
+ this.extra = json.extra.map(entry => new ChatMessage(entry))
64
+ }
65
+ // Text modifiers
66
+ this.bold = json.bold
67
+ this.italic = json.italic
68
+ this.underlined = json.underlined
69
+ this.strikethrough = json.strikethrough
70
+ this.obfuscated = json.obfuscated
108
71
 
109
- // Parse color
110
- this.color = json.color
111
- switch (this.color) {
112
- case 'obfuscated':
113
- this.obfuscated = true
114
- this.color = null
115
- break
116
- case 'bold':
117
- this.bold = true
118
- this.color = null
119
- break
120
- case 'strikethrough':
121
- this.strikethrough = true
122
- this.color = null
123
- break
124
- case 'underlined':
125
- this.underlined = true
126
- this.color = null
127
- break
128
- case 'italic':
129
- this.italic = true
130
- this.color = null
131
- break
132
- case 'reset':
133
- this.reset = true
134
- this.color = null
135
- break
136
- }
137
- if (Array.prototype.indexOf && this.color &&
138
- supportedColors.indexOf(this.color) === -1 && displayWarning) {
139
- console.warn('ChatMessage parsed with unsupported color', this.color)
140
- }
72
+ // Supported constants @ 2014-04-21
73
+ const supportedColors = [
74
+ 'black',
75
+ 'dark_blue',
76
+ 'dark_green',
77
+ 'dark_aqua',
78
+ 'dark_red',
79
+ 'dark_purple',
80
+ 'gold',
81
+ 'gray',
82
+ 'dark_gray',
83
+ 'blue',
84
+ 'green',
85
+ 'aqua',
86
+ 'red',
87
+ 'light_purple',
88
+ 'yellow',
89
+ 'white',
90
+ 'obfuscated',
91
+ 'bold',
92
+ 'strikethrough',
93
+ 'underlined',
94
+ 'italic',
95
+ 'reset'
96
+ ]
97
+ const supportedClick = [
98
+ 'open_url',
99
+ 'open_file',
100
+ 'run_command',
101
+ 'suggest_command'
102
+ ]
103
+ const supportedHover = [
104
+ 'show_text',
105
+ 'show_achievement',
106
+ 'show_item',
107
+ 'show_entity'
108
+ ]
141
109
 
142
- // Parse click event
143
- if (typeof json.clickEvent === 'object') {
144
- this.clickEvent = json.clickEvent
145
- if (typeof this.clickEvent.action !== 'string') {
146
- throw new Error('ClickEvent action missing in ChatMessage')
147
- } else if (Array.prototype.indexOf && supportedClick.indexOf(this.clickEvent.action) === -1 && displayWarning) {
148
- console.warn('ChatMessage parsed with unsupported clickEvent', this.clickEvent.action)
110
+ // Parse color
111
+ this.color = json.color
112
+ switch (this.color) {
113
+ case 'obfuscated':
114
+ this.obfuscated = true
115
+ this.color = null
116
+ break
117
+ case 'bold':
118
+ this.bold = true
119
+ this.color = null
120
+ break
121
+ case 'strikethrough':
122
+ this.strikethrough = true
123
+ this.color = null
124
+ break
125
+ case 'underlined':
126
+ this.underlined = true
127
+ this.color = null
128
+ break
129
+ case 'italic':
130
+ this.italic = true
131
+ this.color = null
132
+ break
133
+ case 'reset':
134
+ this.reset = true
135
+ this.color = null
136
+ break
137
+ }
138
+ // Make sure color is valid
139
+ if (Array.prototype.indexOf && this.color &&
140
+ supportedColors.indexOf(this.color) === -1 &&
141
+ !this.color.match(/#[a-fA-F\d]{6}/) && displayWarning) {
142
+ console.warn('ChatMessage parsed with unsupported color', this.color)
149
143
  }
150
- }
151
144
 
152
- // Parse hover event
153
- if (typeof json.hoverEvent === 'object') {
154
- this.hoverEvent = json.hoverEvent
155
- if (typeof this.hoverEvent.action !== 'string') {
156
- throw new Error('HoverEvent action missing in ChatMessage')
157
- } else if (Array.prototype.indexOf && supportedHover.indexOf(this.hoverEvent.action) === -1 && displayWarning) {
158
- console.warn('ChatMessage parsed with unsupported hoverEvent', this.hoverEvent.action)
145
+ // Parse click event
146
+ if (typeof json.clickEvent === 'object') {
147
+ this.clickEvent = json.clickEvent
148
+ if (typeof this.clickEvent.action !== 'string') {
149
+ throw new Error('ClickEvent action missing in ChatMessage')
150
+ } else if (Array.prototype.indexOf && supportedClick.indexOf(this.clickEvent.action) === -1 && displayWarning) {
151
+ console.warn('ChatMessage parsed with unsupported clickEvent', this.clickEvent.action)
152
+ }
159
153
  }
160
- // Special case
161
- if (this.hoverEvent.action === 'show_item') {
162
- let content
163
- if (this.hoverEvent.value instanceof Array) {
164
- if (this.hoverEvent.value[0] instanceof Object) {
165
- content = this.hoverEvent.value[0].text
166
- } else {
167
- content = this.hoverEvent.value[0]
168
- }
169
- } else {
170
- if (this.hoverEvent.value instanceof Object) {
171
- content = this.hoverEvent.value.text
154
+
155
+ // Parse hover event
156
+ if (typeof json.hoverEvent === 'object') {
157
+ this.hoverEvent = json.hoverEvent
158
+ if (typeof this.hoverEvent.action !== 'string') {
159
+ throw new Error('HoverEvent action missing in ChatMessage')
160
+ } else if (Array.prototype.indexOf && supportedHover.indexOf(this.hoverEvent.action) === -1 && displayWarning) {
161
+ console.warn('ChatMessage parsed with unsupported hoverEvent', this.hoverEvent.action)
162
+ }
163
+ // Special case
164
+ if (this.hoverEvent.action === 'show_item') {
165
+ let content
166
+ if (this.hoverEvent.value instanceof Array) {
167
+ if (this.hoverEvent.value[0] instanceof Object) {
168
+ content = this.hoverEvent.value[0].text
169
+ } else {
170
+ content = this.hoverEvent.value[0]
171
+ }
172
172
  } else {
173
- content = this.hoverEvent.value
173
+ if (this.hoverEvent.value instanceof Object) {
174
+ content = this.hoverEvent.value.text
175
+ } else {
176
+ content = this.hoverEvent.value
177
+ }
174
178
  }
175
- }
176
- try {
177
- this.hoverEvent.value = mojangson.parse(content)
178
- } catch (err) {
179
+ try {
180
+ this.hoverEvent.value = mojangson.parse(content)
181
+ } catch (err) {
179
182
 
183
+ }
180
184
  }
181
185
  }
182
186
  }
183
- }
184
-
185
- /**
186
- * Append one or more ChatMessages
187
- * @param {...object|string} messages ChatMessage
188
- * @return {void}
189
- */
190
- append (...messages) {
191
- if (this.extra === undefined) this.extra = []
192
- messages.forEach((message) => {
193
- if (typeof message === 'object' && !Array.isArray(message)) {
194
- this.extra.push(message)
195
- } else if (typeof message === 'string') {
196
- this.extra.push(new ChatMessage(message))
197
- }
198
- })
199
- }
200
187
 
201
- /**
202
- * Returns a clone of the ChatMessage
203
- * @return {ChatMessage}
204
- */
205
- clone () {
206
- return Object.assign(
207
- Object.create(
208
- Object.getPrototypeOf(this)
209
- ),
210
- this
211
- )
212
- }
188
+ /**
189
+ * Append one or more ChatMessages
190
+ * @param {...object|string} messages ChatMessage
191
+ * @return {void}
192
+ */
193
+ append (...messages) {
194
+ if (this.extra === undefined) this.extra = []
195
+ messages.forEach((message) => {
196
+ if (typeof message === 'object' && !Array.isArray(message)) {
197
+ this.extra.push(message)
198
+ } else if (typeof message === 'string') {
199
+ this.extra.push(new ChatMessage(message))
200
+ }
201
+ })
202
+ }
213
203
 
214
- /**
215
- * Returns the count of text extras and child ChatMessages
216
- * Does not count recursively in to the children
217
- * @return {Number}
218
- */
219
- length () {
220
- let count = 0
221
- if (this.text) count++
222
- else if (this.with) count += this.with.length
204
+ /**
205
+ * Returns a clone of the ChatMessage
206
+ * @return {ChatMessage}
207
+ */
208
+ clone () {
209
+ return new ChatMessage(JSON.parse(JSON.stringify(this.json)))
210
+ }
223
211
 
224
- if (this.extra) count += this.extra.length
225
- return count
226
- }
212
+ /**
213
+ * Returns the count of text extras and child ChatMessages
214
+ * Does not count recursively in to the children
215
+ * @return {Number}
216
+ */
217
+ length () {
218
+ let count = 0
219
+ if (this.text) count++
220
+ else if (this.with) count += this.with.length
227
221
 
228
- /**
229
- * Returns a text part from the message
230
- * @param {Number} idx Index of the part
231
- * @return {String}
232
- */
233
- getText (idx, lang = defaultLang) {
234
- // If the index is not defined is is invalid, return toString
235
- if (typeof idx !== 'number') return this.toString(lang)
236
- // If we are not a translating message, return the text
237
- if (this.text && idx === 0) return this.text.replace(/§[0-9a-flnmokr]/g, '')
238
- // Else return the with child if it's in range
239
- else if (this.with.length > idx) return this.with[idx].toString(lang)
240
- // Else return the extra if it's in range
241
- if (this.extra && this.extra.length + (this.text ? 1 : this.with.length) > idx) {
242
- return this.extra[idx - (this.text ? 1 : this.with.length)].toString(lang)
222
+ if (this.extra) count += this.extra.length
223
+ return count
243
224
  }
244
- // Not sure how you want to default this
245
- // Undefined, an error ?
246
- return ''
247
- }
248
225
 
249
- /**
250
- * Flattens the message in to plain-text
251
- * @return {String}
252
- */
253
- toString (lang = defaultLang) {
254
- let message = ''
255
- if (typeof this.text === 'string' || typeof this.text === 'number') message += this.text
256
- else if (this.with) {
257
- const args = this.with.map(entry => entry.toString(lang))
258
- const format = lang[this.translate]
259
- if (!format) message += args.join('')
260
- else message += vsprintf(format, args)
261
- } else if (this.translate) {
262
- message += lang[this.translate]
263
- }
264
- if (this.extra) {
265
- message += this.extra.map((entry) => entry.toString(lang)).join('')
226
+ /**
227
+ * Returns a text part from the message
228
+ * @param {Number} idx Index of the part
229
+ * @return {String}
230
+ */
231
+ getText (idx, lang = defaultLang) {
232
+ // If the index is not defined is is invalid, return toString
233
+ if (typeof idx !== 'number') return this.toString(lang)
234
+ // If we are not a translating message, return the text
235
+ if (this.text && idx === 0) return this.text.replace(/§[0-9a-flnmokr]/g, '')
236
+ // Else return the with child if it's in range
237
+ else if (this.with.length > idx) return this.with[idx].toString(lang)
238
+ // Else return the extra if it's in range
239
+ if (this.extra && this.extra.length + (this.text ? 1 : this.with.length) > idx) {
240
+ return this.extra[idx - (this.text ? 1 : this.with.length)].toString(lang)
241
+ }
242
+ // Not sure how you want to default this
243
+ // Undefined, an error ?
244
+ return ''
266
245
  }
267
- return message.replace(/§[0-9a-flnmokr]/g, '')
268
- }
269
246
 
270
- valueOf () {
271
- return this.toString()
272
- }
247
+ /**
248
+ * Flattens the message in to plain-text
249
+ * @return {String}
250
+ */
251
+ toString (lang = defaultLang) {
252
+ let message = ''
253
+ if (typeof this.text === 'string' || typeof this.text === 'number') message += this.text
254
+ else if (this.with) {
255
+ const args = this.with.map(entry => entry.toString(lang))
256
+ const format = lang[this.translate]
257
+ if (!format) message += args.join('')
258
+ else message += vsprintf(format, args)
259
+ } else if (this.translate) {
260
+ message += lang[this.translate]
261
+ }
262
+ if (this.extra) {
263
+ message += this.extra.map((entry) => entry.toString(lang)).join('')
264
+ }
265
+ return message.replace(/§[0-9a-flnmokr]/g, '')
266
+ }
273
267
 
274
- toMotd (lang = defaultLang, parent = {}) {
275
- const codes = {
276
- color: {
277
- black: '§0',
278
- dark_blue: '§1',
279
- dark_green: '§2',
280
- dark_aqua: '§3',
281
- dark_red: '§4',
282
- dark_purple: '§5',
283
- gold: '§6',
284
- gray: '§7',
285
- dark_gray: '§8',
286
- blue: '§9',
287
- green: '§a',
288
- aqua: '§b',
289
- red: '§c',
290
- light_purple: '§d',
291
- yellow: '§e',
292
- white: '§f'
293
- },
294
- bold: '§l',
295
- italic: '§o',
296
- underlined: '§n',
297
- strikethrough: '§m',
298
- obfuscated: '§k'
268
+ valueOf () {
269
+ return this.toString()
299
270
  }
300
271
 
301
- let message = Object.keys(codes).map((code) => {
302
- this[code] = this[code] || parent[code]
303
- if (!this[code] || this[code] === 'false') return null
304
- if (code === 'color') return codes.color[this.color]
305
- return codes[code]
306
- }).join('')
272
+ toMotd (lang = defaultLang, parent = {}) {
273
+ const codes = {
274
+ color: {
275
+ black: '§0',
276
+ dark_blue: '§1',
277
+ dark_green: '§2',
278
+ dark_aqua: '§3',
279
+ dark_red: '§4',
280
+ dark_purple: '§5',
281
+ gold: '§6',
282
+ gray: '§7',
283
+ dark_gray: '§8',
284
+ blue: '§9',
285
+ green: '§a',
286
+ aqua: '§b',
287
+ red: '§c',
288
+ light_purple: '§d',
289
+ yellow: '§e',
290
+ white: '§f'
291
+ },
292
+ bold: '§l',
293
+ italic: '§o',
294
+ underlined: '§n',
295
+ strikethrough: '§m',
296
+ obfuscated: '§k'
297
+ }
307
298
 
308
- if (typeof this.text === 'string' || typeof this.text === 'number') message += `${this.text}§r`
309
- else if (this.with) {
310
- const args = this.with.map(entry => entry.toMotd(lang))
311
- const format = lang[this.translate]
312
- if (!format) message += args.join('')
313
- else message += vsprintf(format, args)
314
- } else if (this.translate) {
315
- message += lang[this.translate]
316
- }
317
- if (this.extra) {
318
- message += this.extra.map(entry => entry.toMotd(lang, this)).join('')
319
- }
320
- return message
321
- }
299
+ let message = Object.keys(codes).map((code) => {
300
+ this[code] = this[code] || parent[code]
301
+ if (!this[code] || this[code] === 'false') return null
302
+ if (code === 'color') {
303
+ // return hex codes in this format
304
+ if (this.color.startsWith('#')) return `§${this.color}`
305
+ return codes.color[this.color]
306
+ }
307
+ return codes[code]
308
+ }).join('')
322
309
 
323
- toAnsi (lang = defaultLang) {
324
- const codes = {
325
- '§0': '\u001b[30m',
326
- '§1': '\u001b[34m',
327
- '§2': '\u001b[32m',
328
- '§3': '\u001b[36m',
329
- '§4': '\u001b[31m',
330
- '§5': '\u001b[35m',
331
- '§6': '\u001b[33m',
332
- '§7': '\u001b[37m',
333
- '§8': '\u001b[90m',
334
- '§9': '\u001b[94m',
335
- '§a': '\u001b[92m',
336
- '§b': '\u001b[96m',
337
- '§c': '\u001b[91m',
338
- '§d': '\u001b[95m',
339
- '§e': '\u001b[93m',
340
- '§f': '\u001b[97m',
341
- '§l': '\u001b[1m',
342
- '§o': '\u001b[3m',
343
- '§n': '\u001b[4m',
344
- '§m': '\u001b[9m',
345
- '§k': '\u001b[6m',
346
- '§r': '\u001b[0m'
310
+ if (typeof this.text === 'string' || typeof this.text === 'number') message += `${this.text}§r`
311
+ else if (this.with) {
312
+ const args = this.with.map(entry => entry.toMotd(lang))
313
+ const format = lang[this.translate]
314
+ if (!format) message += args.join('')
315
+ else message += vsprintf(format, args)
316
+ } else if (this.translate) {
317
+ message += lang[this.translate]
318
+ }
319
+ if (this.extra) {
320
+ message += this.extra.map(entry => entry.toMotd(lang, this)).join('')
321
+ }
322
+ return message
347
323
  }
348
324
 
349
- let message = this.toMotd(lang)
350
- for (const k in codes) {
351
- message = message.replace(new RegExp(k, 'g'), codes[k])
325
+ toAnsi (lang = defaultLang) {
326
+ const codes = {
327
+ '§0': '\u001b[30m',
328
+ '§1': '\u001b[34m',
329
+ '§2': '\u001b[32m',
330
+ '§3': '\u001b[36m',
331
+ '§4': '\u001b[31m',
332
+ '§5': '\u001b[35m',
333
+ '§6': '\u001b[33m',
334
+ '§7': '\u001b[37m',
335
+ '§8': '\u001b[90m',
336
+ '§9': '\u001b[94m',
337
+ '§a': '\u001b[92m',
338
+ '§b': '\u001b[96m',
339
+ '§c': '\u001b[91m',
340
+ '§d': '\u001b[95m',
341
+ '§e': '\u001b[93m',
342
+ '§f': '\u001b[97m',
343
+ '§l': '\u001b[1m',
344
+ '§o': '\u001b[3m',
345
+ '§n': '\u001b[4m',
346
+ '§m': '\u001b[9m',
347
+ '§k': '\u001b[6m',
348
+ '§r': '\u001b[0m'
349
+ }
350
+
351
+ let message = this.toMotd(lang)
352
+ for (const k in codes) {
353
+ message = message.replace(new RegExp(k, 'g'), codes[k])
354
+ }
355
+ const hexRegex = /§#?([a-fA-F\d]{2})([a-fA-F\d]{2})([a-fA-F\d]{2})/
356
+ while (message.search(hexRegex) !== -1) {
357
+ // Stolen from https://stackoverflow.com/questions/5623838/rgb-to-hex-and-hex-to-rgb
358
+ const hexCodes = hexRegex.exec(message)
359
+ // Iterate over each hexColorCode match (§#69420, §#ABCDEF, §#A1B2C3)
360
+ const red = parseInt(hexCodes[1], 16)
361
+ const green = parseInt(hexCodes[2], 16)
362
+ const blue = parseInt(hexCodes[3], 16)
363
+ // ANSI from https://gist.github.com/fnky/458719343aabd01cfb17a3a4f7296797#rgb-colors
364
+ message = message.replace(hexRegex, `\u001b[38;2;${red};${green};${blue}m`)
365
+ }
366
+ return message + '\u001b[0m'
352
367
  }
353
- return message + '\u001b[0m'
354
- }
355
368
 
356
- static fromNotch (msg) {
357
- let toRet
358
- try {
359
- toRet = new ChatMessage(JSON.parse(msg))
360
- } catch (e) {
361
- toRet = new ChatMessage(msg)
369
+ static fromNotch (msg) {
370
+ let toRet
371
+ try {
372
+ toRet = new ChatMessage(JSON.parse(msg))
373
+ } catch (e) {
374
+ toRet = new ChatMessage(msg)
375
+ }
376
+ return toRet
362
377
  }
363
- return toRet
364
378
  }
379
+
380
+ ChatMessage.MessageBuilder = MessageBuilder
381
+ return ChatMessage
365
382
  }
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "prismarine-chat",
3
- "version": "1.3.2",
3
+ "version": "1.5.0",
4
4
  "description": "Wrapper for a minecraft chat message",
5
5
  "main": "index.js",
6
6
  "scripts": {
7
- "test": "jest --verbose",
7
+ "test": "mocha --reporter spec --exit",
8
8
  "pretest": "npm run lint",
9
9
  "lint": "standard",
10
10
  "fix": "standard --fix"
@@ -24,7 +24,8 @@
24
24
  },
25
25
  "homepage": "https://github.com/PrismarineJS/prismarine-chat#readme",
26
26
  "devDependencies": {
27
- "jest": "^27.0.4",
27
+ "expect": "^27.3.1",
28
+ "mocha": "^9.1.3",
28
29
  "prismarine-chat": "file:.",
29
30
  "standard": "^16.0.1"
30
31
  },
@@ -32,7 +33,7 @@
32
33
  "minecraft-data": "^2.62.1",
33
34
  "mojangson": "^2.0.1",
34
35
  "prismarine-item": "^1.10.0",
35
- "prismarine-nbt": "^1.6.0",
36
+ "prismarine-nbt": "^2.0.0",
36
37
  "sprintf-js": "^1.1.2"
37
38
  }
38
39
  }
@@ -1,10 +1,13 @@
1
- /* eslint-env jest */
1
+ /* eslint-env mocha */
2
+
2
3
  const ChatMessage = require('prismarine-chat')('1.16')
3
- test('Parsing a chat message', () => {
4
+ const expect = require('expect')
5
+
6
+ it('Parsing a chat message', () => {
4
7
  const msg = new ChatMessage({ text: 'Example chat message' })
5
8
  expect(msg.toString()).toBe('Example chat message')
6
9
  })
7
- test('Parsing message that uses language file & numbers', () => {
10
+ it('Parsing message that uses language file & numbers', () => {
8
11
  const msg = new ChatMessage({ italic: true, color: 'gray', translate: 'chat.type.admin', with: [{ insertion: 'ripwhitescrolls', clickEvent: { action: 'suggest_command', value: '/tell ripwhitescrolls ' }, hoverEvent: { action: 'show_entity', contents: { type: 'minecraft:player', id: '9d9e9257-b812-4332-8426-5e9a0d707392', name: { text: 'ripwhitescrolls' } } }, text: 'ripwhitescrolls' }, { translate: 'commands.clear.success.multiple', with: [256, 2] }] })
9
12
  // test as a string
10
13
  expect(msg.toString()).toBe('[ripwhitescrolls: Removed 256 items from 2 players]')
@@ -17,7 +20,17 @@ test('Parsing message that uses language file & numbers', () => {
17
20
  expect(msg.with[1].with[0].text).toBe(256)
18
21
  expect(msg.with[1].with[1].text).toBe(2)
19
22
  })
20
- test('Parsing a chat message which is an array', () => {
23
+ it('Parsing a chat message which is an array', () => {
21
24
  const msg = new ChatMessage([{ text: 'Example chat ' }, { text: 'message' }])
22
25
  expect(msg.toString()).toBe('Example chat message')
23
26
  })
27
+ it('Chat Message with a single hex color', () => {
28
+ const msg = new ChatMessage({ text: 'uwu', color: '#FF0000' })
29
+ expect(msg.toMotd()).toBe('§#FF0000uwu§r')
30
+ expect(msg.toAnsi()).toBe('\u001B[38;2;255;0;0muwu\u001B[0m\u001B[0m')
31
+ })
32
+ it('Chat Message with multiple hex colors', () => {
33
+ const msg = new ChatMessage(['', { text: 'uwu ', color: '#FF0000' }, { text: 'owo ', color: '#0000FF' }, { text: 'uwu', color: '#FF0000' }])
34
+ expect(msg.toMotd()).toBe('§r§#FF0000uwu §r§#0000FFowo §r§#FF0000uwu§r')
35
+ expect(msg.toAnsi()).toBe('\u001B[0m\u001B[38;2;255;0;0muwu \u001B[0m\u001B[38;2;0;0;255mowo \u001B[0m\u001B[38;2;255;0;0muwu\u001B[0m\u001B[0m')
36
+ })
@@ -1,4 +1,6 @@
1
- /* eslint-env jest */
1
+ /* eslint-env mocha */
2
+
3
+ const expect = require('expect')
2
4
 
3
5
  describe('MessageBuilder', () => {
4
6
  describe('1.16.5', () => {
@@ -18,7 +20,7 @@ describe('MessageBuilder', () => {
18
20
  ['Insertion', "Hi I'm inserted!"]
19
21
  ]
20
22
  for (const [prop, val] of properties) {
21
- test(`builder#set${prop}`, () => { // ex: builder#setBold
23
+ it(`builder#set${prop}`, () => { // ex: builder#setBold
22
24
  const msg = new MessageBuilder()[`set${prop}`](val) // ex: .setBold(true)
23
25
  const json = msg.toJSON() // ex: { bold: true}
24
26
  const propName = prop.toLowerCase() // ex: 'bold'
@@ -28,13 +30,13 @@ describe('MessageBuilder', () => {
28
30
  })
29
31
 
30
32
  describe('with/extra', () => {
31
- test('no translate w/ .with', () => {
33
+ it('no translate w/ .with', () => {
32
34
  const msg = new MessageBuilder()
33
35
  .addWith('Hello,')
34
36
  .addWith('World.')
35
37
  expect(msg.toJSON()).toStrictEqual({})
36
38
  })
37
- test('translate w/ .with', () => {
39
+ it('translate w/ .with', () => {
38
40
  const msg = new MessageBuilder()
39
41
  .setTranslate('chat.type.text')
40
42
  .addWith(new MessageBuilder().setText('U9G'))
@@ -46,7 +48,7 @@ describe('MessageBuilder', () => {
46
48
  expect(text).toStrictEqual('<U9G> Hello world')
47
49
  })
48
50
 
49
- test('w/ .addExtra add split hello & world', () => {
51
+ it('w/ .addExtra add split hello & world', () => {
50
52
  const msg = new MessageBuilder()
51
53
  .addExtra(new MessageBuilder().setText('Hello'))
52
54
  .addExtra(new MessageBuilder().setText(' ').setColor('reset'))