• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

u-wave / core / 20207691175

14 Dec 2025 12:06PM UTC coverage: 85.696% (-0.3%) from 85.96%
20207691175

Pull #730

github

web-flow
Merge e21b60062 into 166408e46
Pull Request #730: Add chat backscroll endpoint

999 of 1190 branches covered (83.95%)

Branch coverage included in aggregate %.

41 of 84 new or added lines in 3 files covered. (48.81%)

3 existing lines in 2 files now uncovered.

10456 of 12177 relevant lines covered (85.87%)

97.78 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

91.08
/src/plugins/chat.js
1
import { randomUUID } from 'node:crypto';
1✔
2
import routes from '../routes/chat.js';
1✔
3
import { now } from '../utils/sqlite.js';
1✔
4

1✔
5
/**
1✔
6
 * @typedef {import('../schema.js').UserID} UserID
1✔
7
 * @typedef {import('../schema.js').User} User
1✔
8
 * @typedef {object} ChatOptions
1✔
9
 * @prop {number} maxLength
1✔
10
 */
1✔
11

1✔
12
/** @type {ChatOptions} */
1✔
13
const defaultOptions = {
1✔
14
  maxLength: 300,
1✔
15
};
1✔
16

1✔
17
class Chat {
1✔
18
  #uw;
154✔
19

154✔
20
  /** @type {ChatOptions} */
154✔
21
  #options;
154✔
22

154✔
23
  /**
154✔
24
   * @param {import('../Uwave.js').default} uw
154✔
25
   * @param {Partial<ChatOptions>} [options]
154✔
26
   */
154✔
27
  constructor(uw, options = {}) {
154✔
28
    this.#uw = uw;
154✔
29

154✔
30
    this.#options = {
154✔
31
      ...defaultOptions,
154✔
32
      ...options,
154✔
33
    };
154✔
34
  }
154✔
35

154✔
36
  /**
154✔
37
   * @param {User} user
154✔
38
   * @param {number} duration - Duration in seconds
154✔
39
   * @param {{ moderator: User }} options
154✔
40
   */
154✔
41
  async mute(user, duration, options) {
154✔
42
    const { db } = this.#uw;
1✔
43

1✔
44
    const expiresAt = new Date(Date.now() + duration * 1000);
1✔
45
    await db.insertInto('mutes')
1✔
46
      .values({
1✔
47
        userID: user.id,
1✔
48
        moderatorID: options.moderator.id,
1✔
49
        expiresAt,
1✔
50
      })
1✔
51
      .execute();
1✔
52

1✔
53
    this.#uw.publish('chat:mute', {
1✔
54
      moderatorID: options.moderator.id,
1✔
55
      userID: user.id,
1✔
56
      duration,
1✔
57
    });
1✔
58
  }
1✔
59

154✔
60
  /**
154✔
61
   * @param {User} user
154✔
62
   * @param {{ moderator: User }} options
154✔
63
   */
154✔
64
  async unmute(user, options) {
154✔
UNCOV
65
    const { db } = this.#uw;
×
66

×
67
    await db.updateTable('mutes')
×
68
      .where('userID', '=', user.id)
×
69
      .where('expiresAt', '>', now)
×
70
      .set({ expiresAt: now, updatedAt: now })
×
71
      .execute();
×
72

×
73
    this.#uw.publish('chat:unmute', {
×
74
      moderatorID: options.moderator.id,
×
75
      userID: user.id,
×
76
    });
×
77
  }
×
78

154✔
79
  /**
154✔
80
   * @param {User} user
154✔
81
   * @private
154✔
82
   */
154✔
83
  async isMuted(user) {
154✔
84
    const { db } = this.#uw;
7✔
85

7✔
86
    const mute = await db.selectFrom('mutes')
7✔
87
      .where('userID', '=', user.id)
7✔
88
      .where('expiresAt', '>', now)
7✔
89
      .selectAll()
7✔
90
      .executeTakeFirst();
7✔
91

7✔
92
    return mute ?? null;
7✔
93
  }
7✔
94

154✔
95
  /**
154✔
96
   * @param {string} message
154✔
97
   * @private
154✔
98
   */
154✔
99
  truncate(message) {
154✔
100
    return message.slice(0, this.#options.maxLength);
6✔
101
  }
6✔
102

154✔
103
  /**
154✔
104
   * @param {User} user
154✔
105
   * @param {string} message
154✔
106
   */
154✔
107
  async send(user, message) {
154✔
108
    if (await this.isMuted(user)) {
7✔
109
      return;
1✔
110
    }
1✔
111

6✔
112
    this.#uw.publish('chat:message', {
6✔
113
      id: randomUUID(),
6✔
114
      userID: user.id,
6✔
115
      message: this.truncate(message),
6✔
116
      timestamp: Date.now(),
6✔
117
    });
6✔
118
  }
7✔
119

154✔
120
  /**
154✔
121
   * @param {{ id: string } | { userID: UserID } | {}} filter
154✔
122
   * @param {{ moderator: User }} options
154✔
123
   */
154✔
124
  delete(filter, options) {
154✔
125
    const deletion = {
6✔
126
      filter: typeof filter === 'string' ? { id: filter } : filter,
6!
127
      moderatorID: options.moderator.id,
6✔
128
    };
6✔
129

6✔
130
    this.#uw.publish('chat:delete', deletion);
6✔
131
  }
6✔
132
}
154✔
133

1✔
134
/**
1✔
135
 * @param {import('../Uwave.js').default} uw
1✔
136
 * @param {Partial<ChatOptions>} [options]
1✔
137
 */
1✔
138
async function chat(uw, options = {}) {
154✔
139
  uw.chat = new Chat(uw, options);
154✔
140
  uw.httpApi.use('/chat', routes());
154✔
141
}
154✔
142

1✔
143
export default chat;
1✔
144
export { Chat };
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc