@ryuu-reinzz/haruka-lib 1.2.21 → 2.0.1

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.
Files changed (44) hide show
  1. package/main/index.js +4 -1
  2. package/main/socket.js +1 -2
  3. package/main/sticker-engine/java-script/Sticker.d.ts +130 -0
  4. package/main/sticker-engine/java-script/Sticker.js +187 -0
  5. package/main/sticker-engine/java-script/Types.d.ts +43 -0
  6. package/main/sticker-engine/java-script/Types.js +1 -0
  7. package/main/sticker-engine/java-script/Utils.d.ts +9 -0
  8. package/main/sticker-engine/java-script/Utils.js +11 -0
  9. package/main/sticker-engine/java-script/extractMetadata.d.ts +6 -0
  10. package/main/sticker-engine/java-script/extractMetadata.js +11 -0
  11. package/main/sticker-engine/java-script/index.d.ts +8 -0
  12. package/main/sticker-engine/java-script/index.js +8 -0
  13. package/main/sticker-engine/java-script/internal/Metadata/Exif.d.ts +9 -0
  14. package/main/sticker-engine/java-script/internal/Metadata/Exif.js +34 -0
  15. package/main/sticker-engine/java-script/internal/Metadata/RawMetadata.d.ts +8 -0
  16. package/main/sticker-engine/java-script/internal/Metadata/RawMetadata.js +9 -0
  17. package/main/sticker-engine/java-script/internal/Metadata/StickerMetadata.d.ts +18 -0
  18. package/main/sticker-engine/java-script/internal/Metadata/StickerMetadata.js +49 -0
  19. package/main/sticker-engine/java-script/internal/Metadata/StickerTypes.d.ts +7 -0
  20. package/main/sticker-engine/java-script/internal/Metadata/StickerTypes.js +8 -0
  21. package/main/sticker-engine/java-script/internal/convert.d.ts +3 -0
  22. package/main/sticker-engine/java-script/internal/convert.js +69 -0
  23. package/main/sticker-engine/java-script/internal/crop.d.ts +2 -0
  24. package/main/sticker-engine/java-script/internal/crop.js +30 -0
  25. package/main/sticker-engine/java-script/internal/imagesToWebp.d.ts +2 -0
  26. package/main/sticker-engine/java-script/internal/imagesToWebp.js +17 -0
  27. package/main/sticker-engine/java-script/internal/videoToGif.d.ts +3 -0
  28. package/main/sticker-engine/java-script/internal/videoToGif.js +16 -0
  29. package/main/sticker-engine/type-script/Sticker.ts +206 -0
  30. package/main/sticker-engine/type-script/Types.ts +189 -0
  31. package/main/sticker-engine/type-script/Utils.ts +12 -0
  32. package/main/sticker-engine/type-script/extractMetadata.ts +13 -0
  33. package/main/sticker-engine/type-script/index.ts +9 -0
  34. package/main/sticker-engine/type-script/internal/Metadata/Exif.ts +39 -0
  35. package/main/sticker-engine/type-script/internal/Metadata/RawMetadata.ts +15 -0
  36. package/main/sticker-engine/type-script/internal/Metadata/StickerMetadata.ts +60 -0
  37. package/main/sticker-engine/type-script/internal/Metadata/StickerTypes.ts +7 -0
  38. package/main/sticker-engine/type-script/internal/convert.ts +87 -0
  39. package/main/sticker-engine/type-script/internal/crop.ts +32 -0
  40. package/main/sticker-engine/type-script/internal/imagesToWebp.ts +19 -0
  41. package/main/sticker-engine/type-script/internal/node-webpmux.d.ts +8 -0
  42. package/main/sticker-engine/type-script/internal/videoToGif.ts +18 -0
  43. package/package.json +12 -3
  44. package/tsconfig.json +13 -0
@@ -0,0 +1,60 @@
1
+ import { Categories, IStickerConfig, IStickerOptions } from '../../Types'
2
+ import Utils from '../../Utils'
3
+
4
+ export default class StickerMetadata implements IStickerOptions {
5
+ public crop = false
6
+ public full = false
7
+ constructor(
8
+ public pack = '',
9
+ public author = '',
10
+ public categories: Categories[] = [],
11
+ public id = Utils.generateStickerID()
12
+ ) {}
13
+
14
+ static from = (object: Partial<StickerMetadata>): StickerMetadata => {
15
+ return new StickerMetadata(object.pack, object.author, object.categories, object.id)
16
+ }
17
+
18
+ public setPack = (title: string): this => {
19
+ this.pack = title
20
+ return this
21
+ }
22
+
23
+ public setAuthor = (author: string): this => {
24
+ this.author = author
25
+ return this
26
+ }
27
+
28
+ public setId = (id: string): this => {
29
+ this.id = id
30
+ return this
31
+ }
32
+
33
+ public setCrop = (value: boolean): this => {
34
+ this.crop = value
35
+ this.full = !value
36
+ return this
37
+ }
38
+
39
+ public setFull = (value: boolean): this => {
40
+ this.crop = !value
41
+ this.full = value
42
+ return this
43
+ }
44
+
45
+ public setCategories = (categories: string | string[]): this => {
46
+ this.categories = (
47
+ typeof categories === 'string' ? categories.split(',').map((emoji) => emoji.trim()) : categories
48
+ ) as Categories[]
49
+ return this
50
+ }
51
+
52
+ public toJSON = (): IStickerConfig => {
53
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
54
+ const obj: any = {}
55
+ Object.keys(this)
56
+ .filter((key) => typeof this[key as keyof this] !== 'function')
57
+ .forEach((key) => (obj[key] = this[key as keyof IStickerConfig] as IStickerConfig[keyof IStickerConfig]))
58
+ return obj as IStickerConfig
59
+ }
60
+ }
@@ -0,0 +1,7 @@
1
+ export enum StickerTypes {
2
+ DEFAULT = 'default',
3
+ CROPPED = 'crop',
4
+ FULL = 'full',
5
+ CIRCLE = 'circle',
6
+ ROUNDED = 'rounded'
7
+ }
@@ -0,0 +1,87 @@
1
+ import sharp, { fit } from 'sharp'
2
+ import videoToGif from './videoToGif'
3
+ import { writeFile } from 'fs-extra'
4
+ import { tmpdir } from 'os'
5
+ import crop from './crop'
6
+ import { StickerTypes } from './Metadata/StickerTypes'
7
+ import { defaultBg } from '../Utils'
8
+ import { IStickerOptions } from '..'
9
+
10
+ const convert = async (
11
+ data: Buffer,
12
+ mime: string,
13
+ { quality = 100, background = defaultBg, type = StickerTypes.DEFAULT }: IStickerOptions
14
+ ): Promise<Buffer> => {
15
+ const isVideo = mime.startsWith('video')
16
+ let image = isVideo ? await videoToGif(data) : data
17
+ const isAnimated = isVideo || mime.includes('gif') || mime.includes('webp')
18
+
19
+ if (isAnimated && ['crop', 'circle', 'rouded'].includes(type)) {
20
+ const filename = `${tmpdir()}/${Math.random().toString(36)}.webp`
21
+ await writeFile(filename, image)
22
+ ;[image, type] = [
23
+ await crop(filename),
24
+ type === StickerTypes.CIRCLE
25
+ ? StickerTypes.CIRCLE
26
+ : type === StickerTypes.ROUNDED
27
+ ? StickerTypes.ROUNDED
28
+ : StickerTypes.DEFAULT
29
+ ]
30
+ }
31
+
32
+ const img = sharp(image, { animated: isAnimated }).toFormat('webp')
33
+
34
+ switch (type) {
35
+ case StickerTypes.CROPPED:
36
+ img.resize(512, 512, {
37
+ fit: fit.cover
38
+ })
39
+ break
40
+
41
+ case StickerTypes.FULL:
42
+ img.resize(512, 512, {
43
+ fit: fit.contain,
44
+ background
45
+ })
46
+ break
47
+
48
+ case StickerTypes.CIRCLE:
49
+ img.resize(512, 512, {
50
+ fit: fit.cover
51
+ }).composite([
52
+ {
53
+ input: Buffer.from(
54
+ `<svg width="512" height="512"><circle cx="256" cy="256" r="256" fill="${background}"/></svg>`
55
+ ),
56
+ blend: 'dest-in',
57
+ gravity: 'northeast',
58
+ tile: true
59
+ }
60
+ ])
61
+ break
62
+
63
+ case StickerTypes.ROUNDED:
64
+ img.resize(512, 512, {
65
+ fit: fit.cover
66
+ }).composite([
67
+ {
68
+ input: Buffer.from(
69
+ `<svg width="512" height="512"><rect rx="50" ry="50" width="512" height="512" fill="${background}"/></svg>`
70
+ ),
71
+ blend: 'dest-in',
72
+ gravity: 'northeast',
73
+ tile: true
74
+ }
75
+ ])
76
+ break
77
+ }
78
+
79
+ return await img
80
+ .webp({
81
+ quality,
82
+ lossless: false
83
+ })
84
+ .toBuffer()
85
+ }
86
+
87
+ export default convert
@@ -0,0 +1,32 @@
1
+ import Ffmpeg from 'fluent-ffmpeg'
2
+ import { readFile } from 'fs-extra'
3
+ import { tmpdir } from 'os'
4
+
5
+ const crop = async (filename: string): Promise<Buffer> => {
6
+ const file = await new Promise<string>((resolve) => {
7
+ const name = `${tmpdir()}/${Math.random().toString(36)}.webp`
8
+ Ffmpeg(filename)
9
+ // eslint-disable-next-line no-useless-escape
10
+ .outputOptions([
11
+ '-vcodec',
12
+ 'libwebp',
13
+ '-vf',
14
+ // eslint-disable-next-line no-useless-escape
15
+ `crop=w='min(min(iw\,ih)\,500)':h='min(min(iw\,ih)\,500)',scale=500:500,setsar=1,fps=15`,
16
+ '-loop',
17
+ '0',
18
+ '-preset',
19
+ 'default',
20
+ '-an',
21
+ '-vsync',
22
+ '0',
23
+ '-s',
24
+ '512:512'
25
+ ])
26
+ .save(name)
27
+ .on('end', () => resolve(name))
28
+ })
29
+ return await readFile(file)
30
+ }
31
+
32
+ export default crop
@@ -0,0 +1,19 @@
1
+ import Ffmpeg from 'fluent-ffmpeg'
2
+ import { readFile } from 'fs-extra'
3
+ import { tmpdir } from 'os'
4
+
5
+ const imagesToWebp = async (filename: string): Promise<Buffer> => {
6
+ const file = await new Promise<string>((resolve) => {
7
+ const name = `${tmpdir()}/${Math.random().toString(36)}.webp`
8
+ Ffmpeg(filename)
9
+ .outputOption('-lavfi split[v],palettegen,[v]paletteuse')
10
+ .outputOption('-vcodec libwebp')
11
+ .outputFPS(10)
12
+ .loop(0)
13
+ .save(name)
14
+ .on('end', () => resolve(name))
15
+ })
16
+ return await readFile(file)
17
+ }
18
+
19
+ export default imagesToWebp
@@ -0,0 +1,8 @@
1
+ declare module 'node-webpmux' {
2
+ export class Image {
3
+ constructor()
4
+ exif: Buffer
5
+ load(buffer: Buffer | string): Promise<void>
6
+ save(...args: unknown[]): Promise<Buffer>
7
+ }
8
+ }
@@ -0,0 +1,18 @@
1
+ import ffmpeg from 'fluent-ffmpeg'
2
+ import { writeFile, readFile, unlink } from 'fs-extra'
3
+ import { tmpdir } from 'os'
4
+
5
+ /** https://stackoverflow.com/questions/52156713/fluent-ffmpeg-h264-to-gif-throwing-error-1 */
6
+ const videoToGif = async (data: Buffer): Promise<Buffer> => {
7
+ const filename = `${tmpdir()}/${Math.random().toString(36)}`
8
+ const [video, gif] = ['video', 'gif'].map((ext) => `${filename}.${ext}`)
9
+ await writeFile(video, data)
10
+ await new Promise((resolve) => {
11
+ ffmpeg(video).save(gif).on('end', resolve)
12
+ })
13
+ const buffer = await readFile(gif)
14
+ ;[video, gif].forEach((file) => unlink(file))
15
+ return buffer
16
+ }
17
+
18
+ export default videoToGif
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ryuu-reinzz/haruka-lib",
3
- "version": "1.2.21",
3
+ "version": "2.0.1",
4
4
  "description": "Library extra for bot WhatsApp",
5
5
  "main": "main/index.js",
6
6
  "type": "module",
@@ -15,8 +15,17 @@
15
15
  ":v"
16
16
  ],
17
17
  "dependencies": {
18
+ "axios": "^1.12.1",
18
19
  "better-sqlite3": "^12.5.0",
19
- "node-fetch": "^2.6.1",
20
- "wa-sticker-formatter": "4.4.4"
20
+ "file-type": "^16.5.3",
21
+ "fluent-ffmpeg": "^2.1.3",
22
+ "fs-extra": "^11.2.0",
23
+ "node-fetch": "^3.3.2",
24
+ "node-webpmux": "^3.2.1",
25
+ "sharp": "^0.34.1"
26
+ },
27
+ "devDependencies": {
28
+ "@types/node": "^25.0.3",
29
+ "typescript": "^5.9.3"
21
30
  }
22
31
  }
package/tsconfig.json ADDED
@@ -0,0 +1,13 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "module": "ESNext",
5
+ "rootDir": "main/sticker-engine/type-script",
6
+ "outDir": "main/sticker-engine/java-script",
7
+ "allowJs": true,
8
+ "esModuleInterop": true,
9
+ "declaration": true,
10
+ "moduleResolution": "node"
11
+ },
12
+ "include": ["main/sticker-engine/type-script/**/*"]
13
+ }