← スキル一覧に戻る

messenger-chat
by Smarter-Poker
Smarter-Poker-World-Hub
⭐ 0🍴 0📅 2026年1月26日
SKILL.md
name: Messenger & Chat description: Real-time messaging, DMs, and group chats
Messenger & Chat Skill
Overview
Build real-time messaging with direct messages, group chats, and table chat.
Message Types
| Type | Description |
|---|---|
| DM | Direct 1-on-1 message |
| GROUP | Group chat (2+ users) |
| TABLE | Poker table chat |
| CLUB | Club-wide chat |
| SYSTEM | System notifications |
Database Schema
CREATE TABLE conversations (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
type TEXT DEFAULT 'DM', -- 'DM', 'GROUP', 'TABLE', 'CLUB'
name TEXT, -- For groups
created_at TIMESTAMPTZ DEFAULT NOW(),
last_message_at TIMESTAMPTZ
);
CREATE TABLE conversation_members (
conversation_id UUID REFERENCES conversations(id),
user_id UUID REFERENCES auth.users(id),
joined_at TIMESTAMPTZ DEFAULT NOW(),
last_read_at TIMESTAMPTZ,
PRIMARY KEY (conversation_id, user_id)
);
CREATE TABLE messages (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
conversation_id UUID REFERENCES conversations(id),
sender_id UUID REFERENCES auth.users(id),
content TEXT NOT NULL,
message_type TEXT DEFAULT 'text', -- 'text', 'image', 'gif', 'system'
metadata JSONB DEFAULT '{}',
created_at TIMESTAMPTZ DEFAULT NOW(),
edited_at TIMESTAMPTZ
);
CREATE INDEX idx_messages_conversation ON messages(conversation_id, created_at DESC);
Send Message
async function sendMessage(conversationId, senderId, content, type = 'text') {
const { data: message, error } = await supabase
.from('messages')
.insert({
conversation_id: conversationId,
sender_id: senderId,
content,
message_type: type
})
.select(`
*,
sender:profiles!sender_id(username, avatar_url)
`)
.single();
// Update conversation last_message_at
await supabase.from('conversations')
.update({ last_message_at: new Date() })
.eq('id', conversationId);
// Broadcast to channel
await supabase.channel(`conversation:${conversationId}`)
.send({
type: 'broadcast',
event: 'new_message',
payload: message
});
return message;
}
Real-time Listener
function useMessages(conversationId) {
const [messages, setMessages] = useState([]);
useEffect(() => {
// Fetch initial messages
fetchMessages(conversationId).then(setMessages);
// Subscribe to new messages
const channel = supabase.channel(`conversation:${conversationId}`)
.on('broadcast', { event: 'new_message' }, ({ payload }) => {
setMessages(prev => [...prev, payload]);
})
.subscribe();
return () => channel.unsubscribe();
}, [conversationId]);
return messages;
}
Start Conversation
async function startConversation(userId, recipientId) {
// Check if DM exists
const existing = await findExistingDM(userId, recipientId);
if (existing) return existing;
// Create new conversation
const { data: conversation } = await supabase
.from('conversations')
.insert({ type: 'DM' })
.select()
.single();
// Add members
await supabase.from('conversation_members').insert([
{ conversation_id: conversation.id, user_id: userId },
{ conversation_id: conversation.id, user_id: recipientId }
]);
return conversation;
}
Unread Count
async function getUnreadCount(userId) {
const { data } = await supabase
.from('conversation_members')
.select(`
conversation_id,
last_read_at,
conversations!inner(last_message_at)
`)
.eq('user_id', userId);
return data.filter(c =>
c.conversations.last_message_at > (c.last_read_at || new Date(0))
).length;
}
Components
ConversationList.jsx- List of chatsMessageThread.jsx- Message displayMessageInput.jsx- Compose messageChatBubble.jsx- Single messageOnlineIndicator.jsx- User online status
スコア
総合スコア
50/100
リポジトリの品質指標に基づく評価
✓SKILL.md
SKILL.mdファイルが含まれている
+20
○LICENSE
ライセンスが設定されている
0/10
○説明文
100文字以上の説明がある
0/10
○人気
GitHub Stars 100以上
0/15
○最近の活動
3ヶ月以内に更新がある
0/10
○フォーク
10回以上フォークされている
0/5
✓Issue管理
オープンIssueが50未満
+5
✓言語
プログラミング言語が設定されている
+5
○タグ
1つ以上のタグが設定されている
0/5
レビュー
💬
レビュー機能は近日公開予定です