Message Metadata Queries
Content: Message Metadata Queries
User Intent
"How do I query Slack/Teams/Discord messages by channel, author, mentions, etc.?"
Operation
SDK Method:
queryContents()with message-specific patternsGraphQL:
queryContentsqueryEntity Type: Content (type: MESSAGE)
Common Use Cases: Find messages by channel, author queries, mentions detection, link analysis
Message Metadata Structure
Messages (from Slack, Teams, Discord) have metadata in the message field:
interface MessageMetadata {
identifier: string; // Message ID
conversationIdentifier: string; // Thread/conversation ID
channelIdentifier: string; // Channel ID
channelName: string; // Channel name
author: PersonReference; // Message author
mentions: PersonReference[]; // @mentioned users
attachmentCount: number;
links: string[]; // URLs in message
}TypeScript (Canonical)
import { Graphlit } from 'graphlit-client';
import { ContentTypes, SearchTypes } from 'graphlit-client/dist/generated/graphql-types';
const graphlit = new Graphlit();
// Query all messages
const allMessages = await graphlit.queryContents({
filter: {
types: [ContentTypes.Message]
}
});
// Search messages by keyword
const searchMessages = await graphlit.queryContents({
search: "product roadmap",
filter: {
types: [ContentTypes.Message]
}
});
// Recent messages
const recentMessages = await graphlit.queryContents({
filter: {
types: [ContentTypes.Message],
createdInLast: 'P7D' // Last 7 days
}
});
// Messages from specific feed (e.g., Slack workspace)
const slackMessages = await graphlit.queryContents({
filter: {
types: [ContentTypes.Message],
feeds: [{ id: 'slack-feed-id' }]
}
});
console.log(`Found ${slackMessages.contents.results.length} messages`);
// Access message metadata
slackMessages.contents.results.forEach(msg => {
if (msg.message) {
console.log(`Channel: ${msg.message.channelName}`);
console.log(`Author: ${msg.message.author?.name}`);
console.log(`Message: ${msg.markdown?.substring(0, 100)}...`);
}
});Query Patterns
1. Filter by Channel
// Get messages from all channels
const messages = await graphlit.queryContents({
filter: {
types: [ContentTypes.Message]
},
limit: 500
});
// Filter by channel name
const engineering = messages.contents.results.filter(msg =>
msg.message?.channelName === 'engineering'
);
const product = messages.contents.results.filter(msg =>
msg.message?.channelName === 'product'
);
console.log(`Engineering messages: ${engineering.length}`);
console.log(`Product messages: ${product.length}`);
// List all channels
const channels = new Set(
messages.contents.results
.map(m => m.message?.channelName)
.filter(Boolean)
);
console.log(`Channels: ${Array.from(channels).join(', ')}`);2. Find by Author
// Search by author name
const byAuthor = await graphlit.queryContents({
search: "Kirk Marple",
searchType: SearchTypes.Keyword,
filter: {
types: [ContentTypes.Message]
}
});
// Filter by author email
const messages = await graphlit.queryContents({
filter: {
types: [ContentTypes.Message]
}
});
const kirkMessages = messages.contents.results.filter(msg =>
msg.message?.author?.email === "[email protected]"
);
console.log(`Kirk's messages: ${kirkMessages.length}`);
// Count messages by author
const byAuthorCount = new Map<string, number>();
messages.contents.results.forEach(msg => {
if (msg.message?.author) {
const author = msg.message.author.name || msg.message.author.email;
byAuthorCount.set(author, (byAuthorCount.get(author) || 0) + 1);
}
});
console.log('\nTop 10 authors:');
Array.from(byAuthorCount.entries())
.sort((a, b) => b[1] - a[1])
.slice(0, 10)
.forEach(([author, count]) => {
console.log(` ${author}: ${count} messages`);
});3. Find Mentions
// Get all messages
const messages = await graphlit.queryContents({
filter: {
types: [ContentTypes.Message]
}
});
// Find messages mentioning specific user
const mentioningKirk = messages.contents.results.filter(msg =>
msg.message?.mentions?.some(m => m.email === "[email protected]")
);
console.log(`Messages mentioning Kirk: ${mentioningKirk.length}`);
// Find messages with any mentions
const withMentions = messages.contents.results.filter(msg =>
msg.message?.mentions && msg.message.mentions.length > 0
);
console.log(`Messages with mentions: ${withMentions.length}`);
// Count mentions per person
const mentionCounts = new Map<string, number>();
messages.contents.results.forEach(msg => {
msg.message?.mentions?.forEach(mention => {
const name = mention.name || mention.email;
mentionCounts.set(name, (mentionCounts.get(name) || 0) + 1);
});
});
console.log('\nMost mentioned people:');
Array.from(mentionCounts.entries())
.sort((a, b) => b[1] - a[1])
.slice(0, 10)
.forEach(([person, count]) => {
console.log(` ${person}: ${count} mentions`);
});4. Thread/Conversation Queries
// Get all messages
const messages = await graphlit.queryContents({
filter: {
types: [ContentTypes.Message]
}
});
// Group by conversation/thread
const conversations = new Map<string, any[]>();
messages.contents.results.forEach(msg => {
if (msg.message?.conversationIdentifier) {
const convId = msg.message.conversationIdentifier;
if (!conversations.has(convId)) {
conversations.set(convId, []);
}
conversations.get(convId)?.push(msg);
}
});
console.log(`Found ${conversations.size} conversations`);
// Find longest threads
const sortedConversations = Array.from(conversations.entries())
.sort((a, b) => b[1].length - a[1].length);
console.log('\nTop 5 longest threads:');
sortedConversations.slice(0, 5).forEach(([convId, messages]) => {
const channel = messages[0].message?.channelName;
const firstMessage = messages[0].markdown?.substring(0, 50);
console.log(`${messages.length} messages in #${channel}: "${firstMessage}..."`);
});
// Find replies (where identifier !== conversationIdentifier)
const replies = messages.contents.results.filter(msg =>
msg.message?.identifier !== msg.message?.conversationIdentifier
);
console.log(`\nReplies: ${replies.length} out of ${messages.contents.results.length} messages`);5. Messages with Links
// Get messages
const messages = await graphlit.queryContents({
filter: {
types: [ContentTypes.Message]
}
});
// Filter messages with links
const withLinks = messages.contents.results.filter(msg =>
msg.message?.links && msg.message.links.length > 0
);
console.log(`Messages with links: ${withLinks.length}`);
// Extract all shared links
const allLinks = new Set<string>();
messages.contents.results.forEach(msg => {
msg.message?.links?.forEach(link => allLinks.add(link));
});
console.log(`Unique links shared: ${allLinks.size}`);
// Most shared domains
const domains = new Map<string, number>();
allLinks.forEach(link => {
try {
const url = new URL(link);
domains.set(url.hostname, (domains.get(url.hostname) || 0) + 1);
} catch (e) {
// Invalid URL
}
});
console.log('\nMost shared domains:');
Array.from(domains.entries())
.sort((a, b) => b[1] - a[1])
.slice(0, 10)
.forEach(([domain, count]) => {
console.log(` ${domain}: ${count} links`);
});6. Messages with Attachments
// Get messages
const messages = await graphlit.queryContents({
filter: {
types: [ContentTypes.Message]
}
});
// Filter by attachments
const withAttachments = messages.contents.results.filter(msg =>
msg.message && msg.message.attachmentCount > 0
);
console.log(`Messages with attachments: ${withAttachments.length}`);
// Distribution
const attachmentDist = new Map<number, number>();
withAttachments.forEach(msg => {
const count = msg.message?.attachmentCount || 0;
attachmentDist.set(count, (attachmentDist.get(count) || 0) + 1);
});
console.log('\nAttachment distribution:');
Array.from(attachmentDist.entries())
.sort((a, b) => a[0] - b[0])
.forEach(([count, messages]) => {
console.log(` ${count} attachment(s): ${messages} messages`);
});7. Channel Activity Analysis
// Get messages
const messages = await graphlit.queryContents({
filter: {
types: [ContentTypes.Message],
createdInLast: 'P30D' // Last 30 days
},
limit: 1000
});
// Messages per channel
const byChannel = new Map<string, number>();
messages.contents.results.forEach(msg => {
const channel = msg.message?.channelName || 'unknown';
byChannel.set(channel, (byChannel.get(channel) || 0) + 1);
});
console.log('Channel activity (last 30 days):');
Array.from(byChannel.entries())
.sort((a, b) => b[1] - a[1])
.forEach(([channel, count]) => {
console.log(` #${channel}: ${count} messages`);
});
// Active hours
const byHour = new Array(24).fill(0);
messages.contents.results.forEach(msg => {
const hour = new Date(msg.creationDate).getHours();
byHour[hour]++;
});
console.log('\nMost active hours:');
byHour
.map((count, hour) => ({ hour, count }))
.sort((a, b) => b.count - a.count)
.slice(0, 5)
.forEach(({ hour, count }) => {
console.log(` ${hour}:00 - ${count} messages`);
});8. Collaboration Patterns
// Get messages
const messages = await graphlit.queryContents({
filter: {
types: [ContentTypes.Message]
}
});
// Build interaction matrix (who mentions whom)
const interactions = new Map<string, Map<string, number>>();
messages.contents.results.forEach(msg => {
const author = msg.message?.author?.email;
if (!author) return;
msg.message?.mentions?.forEach(mention => {
if (!interactions.has(author)) {
interactions.set(author, new Map());
}
const authorInteractions = interactions.get(author)!;
const mentionEmail = mention.email;
authorInteractions.set(mentionEmail, (authorInteractions.get(mentionEmail) || 0) + 1);
});
});
// Find strongest collaborations
console.log('Strongest collaborations:');
const collaborations: Array<{ from: string; to: string; count: number }> = [];
interactions.forEach((targets, source) => {
targets.forEach((count, target) => {
collaborations.push({ from: source, to: target, count });
});
});
collaborations
.sort((a, b) => b.count - a.count)
.slice(0, 10)
.forEach(({ from, to, count }) => {
console.log(` ${from} → ${to}: ${count} mentions`);
});Query messages
messages = await graphlit.queryContents( filter=ContentFilterInput( types=[ContentTypes.Message] ) )
From specific feed
slack_messages = await graphlit.queryContents( filter=ContentFilterInput( types=[ContentTypes.Message], feeds=[EntityReferenceInput(id='slack-feed-id')] ) )
Access metadata
for msg in messages.contents.results: if msg.message: print(f"Channel: {msg.message.channel_name}") print(f"Author: {msg.message.author.name}") if msg.message.mentions: print(f"Mentions: {len(msg.message.mentions)}")
**C#**:
```csharp
using Graphlit;
var client = new Graphlit();
// Query messages
var messages = await graphlit.QueryContents(new ContentFilter
{
Filter = new ContentCriteria
{
Types = new[] { ContentTypes.Message }
}
});
// From specific feed
var slackMessages = await graphlit.QueryContents(new ContentFilter
{
Filter = new ContentCriteria
{
Types = new[] { ContentTypes.Message },
Feeds = new[] { new EntityReference { Id = "slack-feed-id" } }
}
});
// Access metadata
foreach (var msg in messages.Contents.Results)
{
if (msg.Message != null)
{
Console.WriteLine($"Channel: {msg.Message.ChannelName}");
Console.WriteLine($"Author: {msg.Message.Author.Name}");
if (msg.Message.Mentions != null)
{
Console.WriteLine($"Mentions: {msg.Message.Mentions.Length}");
}
}
}Developer Hints
Channel Names are Searchable
// Search for channel name
const results = await graphlit.queryContents({
search: "engineering",
filter: { types: [ContentTypes.Message] }
});
// Or filter after query
const all = await graphlit.queryContents({
filter: { types: [ContentTypes.Message] }
});
const engineering = all.contents.results.filter(
m => m.message?.channelName === 'engineering'
);Mentions Array
// Mentions is array of PersonReference
message.mentions.forEach(mention => {
console.log(`@${mention.name} (${mention.email})`);
});Thread Detection
// Message is thread reply if:
if (message.identifier !== message.conversationIdentifier) {
console.log('This is a reply in a thread');
}Common Issues & Solutions
Issue: Can't filter by specific channel in query Solution: Query all messages, filter client-side
const all = await graphlit.queryContents({
filter: { types: [ContentTypes.Message] }
});
const engineering = all.contents.results.filter(
m => m.message?.channelName === 'engineering'
);Issue: Want to find all messages from one user to another Solution: Filter by author and mentions
const messages = await graphlit.queryContents({
filter: { types: [ContentTypes.Message] }
});
const kirkToMaria = messages.contents.results.filter(msg =>
msg.message?.author?.email === '[email protected]' &&
msg.message?.mentions?.some(m => m.email === '[email protected]')
);Issue: Need to count messages per channel Solution: Query and aggregate
const messages = await graphlit.queryContents({
filter: { types: [ContentTypes.Message] }
});
const byChannel = new Map<string, number>();
messages.contents.results.forEach(msg => {
const channel = msg.message?.channelName || 'unknown';
byChannel.set(channel, (byChannel.get(channel) || 0) + 1);
});Production Example
async function analyzeTeamCommunication() {
console.log('\n=== TEAM COMMUNICATION ANALYSIS ===\n');
// Get recent messages
const messages = await graphlit.queryContents({
filter: {
types: [ContentTypes.Message],
createdInLast: 'P30D'
},
limit: 1000
});
console.log(`Total messages (last 30 days): ${messages.contents.results.length}`);
// Channel breakdown
const channels = new Map<string, number>();
messages.contents.results.forEach(msg => {
const ch = msg.message?.channelName || 'unknown';
channels.set(ch, (channels.get(ch) || 0) + 1);
});
console.log(`\nActive channels: ${channels.size}`);
console.log('Top 10 channels:');
Array.from(channels.entries())
.sort((a, b) => b[1] - a[1])
.slice(0, 10)
.forEach(([channel, count]) => {
console.log(` #${channel}: ${count} messages`);
});
// Author activity
const authors = new Map<string, number>();
messages.contents.results.forEach(msg => {
const author = msg.message?.author?.name || 'unknown';
authors.set(author, (authors.get(author) || 0) + 1);
});
console.log(`\nActive authors: ${authors.size}`);
console.log('Top 10 authors:');
Array.from(authors.entries())
.sort((a, b) => b[1] - a[1])
.slice(0, 10)
.forEach(([author, count]) => {
console.log(` ${author}: ${count} messages`);
});
// Mention statistics
const mentioned = new Map<string, number>();
let totalMentions = 0;
messages.contents.results.forEach(msg => {
msg.message?.mentions?.forEach(mention => {
const name = mention.name || mention.email;
mentioned.set(name, (mentioned.get(name) || 0) + 1);
totalMentions++;
});
});
console.log(`\nTotal mentions: ${totalMentions}`);
console.log('Most mentioned:');
Array.from(mentioned.entries())
.sort((a, b) => b[1] - a[1])
.slice(0, 10)
.forEach(([person, count]) => {
console.log(` ${person}: ${count} mentions`);
});
// Collaboration score
const collaborations = new Map<string, Set<string>>();
messages.contents.results.forEach(msg => {
const author = msg.message?.author?.email;
if (author) {
if (!collaborations.has(author)) {
collaborations.set(author, new Set());
}
msg.message?.mentions?.forEach(m => {
collaborations.get(author)!.add(m.email);
});
}
});
console.log(`\n Collaboration Matrix:`);
Array.from(collaborations.entries())
.sort((a, b) => b[1].size - a[1].size)
.slice(0, 5)
.forEach(([author, collaborators]) => {
console.log(` ${author}: works with ${collaborators.size} people`);
});
}
await analyzeTeamCommunication();Last updated
Was this helpful?