Skip to content

Commit 6a3a633

Browse files
committed
Slack bot messages, fix indexing gui
1 parent 985c469 commit 6a3a633

File tree

5 files changed

+101
-50
lines changed

5 files changed

+101
-50
lines changed

app/data_source/sources/slack/slack.py

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ def validate_config(config: Dict) -> None:
4949

5050
@staticmethod
5151
def _is_valid_message(message: Dict) -> bool:
52-
return 'client_msg_id' in message
52+
return 'client_msg_id' in message or 'bot_id' in message
5353

5454
def __init__(self, *args, **kwargs):
5555
super().__init__(*args, **kwargs)
@@ -62,15 +62,15 @@ def _list_conversations(self) -> List[SlackConversation]:
6262
return [SlackConversation(id=conv['id'], name=conv['name'])
6363
for conv in conversations['channels']]
6464

65-
def _join_conversations(self, conversations: List[SlackConversation]) -> List[SlackConversation]:
65+
def _feed_conversations(self, conversations: List[SlackConversation]) -> List[SlackConversation]:
6666
joined_conversations = []
6767

6868
for conv in conversations:
6969
try:
7070
result = self._slack.conversations_join(channel=conv.id)
7171
if result['ok']:
72-
logger.info(f'Joined channel {conv.name}')
73-
joined_conversations.append(conv)
72+
logger.info(f'Joined channel {conv.name}, adding a fetching task...')
73+
self.add_task_to_queue(self._feed_conversation, conv=conv)
7474
except Exception as e:
7575
logger.warning(f'Could not join channel {conv.name}: {e}')
7676

@@ -92,11 +92,7 @@ def _feed_new_documents(self) -> None:
9292
conversations = self._list_conversations()
9393
logger.info(f'Found {len(conversations)} conversations')
9494

95-
joined_conversations = self._join_conversations(conversations)
96-
logger.info(f'Joined {len(joined_conversations)} conversations')
97-
98-
for conv in joined_conversations:
99-
self.add_task_to_queue(self._feed_conversation, conv=conv)
95+
self._feed_conversations(conversations)
10096

10197
def _feed_conversation(self, conv: SlackConversation):
10298
logger.info(f'Feeding conversation {conv.name}')
@@ -112,8 +108,14 @@ def _feed_conversation(self, conv: SlackConversation):
112108
continue
113109

114110
text = message['text']
115-
author_id = message['user']
116-
author = self._get_author_details(author_id)
111+
if author_id := message.get('user'):
112+
author = self._get_author_details(author_id)
113+
elif message.get('bot_id'):
114+
author = SlackAuthor(name=message.get('username'), image_url=message.get('icons', {}).get('image_48'))
115+
else:
116+
logger.warning(f'Unknown message author: {message}')
117+
continue
118+
117119
if last_msg is not None:
118120
if last_msg.author == author.name:
119121
last_msg.content += f"\n{text}"
@@ -123,7 +125,7 @@ def _feed_conversation(self, conv: SlackConversation):
123125
last_msg = None
124126

125127
timestamp = message['ts']
126-
message_id = message['client_msg_id']
128+
message_id = message.get('client_msg_id') or timestamp
127129
readable_timestamp = datetime.datetime.fromtimestamp(float(timestamp))
128130
message_url = f"https://slack.com/app_redirect?channel={conv.id}&message_ts={timestamp}"
129131
last_msg = BasicDocument(title=author.name, content=text, author=author.name,

app/indexing/background_indexer.py

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,16 @@ class BackgroundIndexer:
1313
_thread = None
1414
_stop_event = threading.Event()
1515
_currently_indexing_count = 0
16+
_total_indexed_count = 0
1617

1718
@classmethod
18-
def get_currently_indexing(cls):
19+
def get_currently_indexing(cls) -> int:
1920
return cls._currently_indexing_count
2021

22+
@classmethod
23+
def get_indexed_count(cls) -> int:
24+
return cls._total_indexed_count
25+
2126
@classmethod
2227
def start(cls):
2328
cls._thread = threading.Thread(target=cls.run)
@@ -39,16 +44,20 @@ def run():
3944
logger.info(f'Background indexer started...')
4045

4146
while not BackgroundIndexer._stop_event.is_set():
42-
queue_items = docs_queue_instance.consume_all()
43-
if not queue_items:
44-
continue
47+
try:
48+
queue_items = docs_queue_instance.consume_all()
49+
if not queue_items:
50+
continue
4551

46-
BackgroundIndexer._currently_indexing_count = len(queue_items)
47-
logger.info(f'Got chunk of {len(queue_items)} documents')
52+
BackgroundIndexer._currently_indexing_count = len(queue_items)
53+
logger.info(f'Got chunk of {len(queue_items)} documents')
4854

49-
docs = [doc.doc for doc in queue_items]
50-
Indexer.index_documents(docs)
51-
BackgroundIndexer._ack_chunk(docs_queue_instance, [doc.queue_item_id for doc in queue_items])
55+
docs = [doc.doc for doc in queue_items]
56+
Indexer.index_documents(docs)
57+
BackgroundIndexer._ack_chunk(docs_queue_instance, [doc.queue_item_id for doc in queue_items])
58+
except Exception as e:
59+
logger.exception(e)
60+
logger.error('Error while indexing documents...')
5261

5362
@staticmethod
5463
def _ack_chunk(queue: IndexQueue, ids: List[int]):
@@ -57,4 +66,5 @@ def _ack_chunk(queue: IndexQueue, ids: List[int]):
5766
queue.ack(id=item_id)
5867

5968
logger.info(f'Acked {len(ids)} documents.')
69+
BackgroundIndexer._total_indexed_count += len(ids)
6070
BackgroundIndexer._currently_indexing_count = 0

app/main.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,14 +114,16 @@ async def shutdown_event():
114114

115115

116116
@app.get("/api/v1/status")
117-
async def status():
117+
def status():
118118
@dataclass
119119
class Status:
120120
docs_in_indexing: int
121121
docs_left_to_index: int
122+
docs_indexed: int
122123

123124
return Status(docs_in_indexing=BackgroundIndexer.get_currently_indexing(),
124-
docs_left_to_index=IndexQueue.get_instance().qsize() + TaskQueue.get_instance().qsize())
125+
docs_left_to_index=IndexQueue.get_instance().qsize() + TaskQueue.get_instance().qsize(),
126+
docs_indexed=BackgroundIndexer.get_indexed_count())
125127

126128

127129
@app.post("/clear-index")

ui/src/App.tsx

Lines changed: 47 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -40,17 +40,21 @@ export interface AppState {
4040
isServerDown: boolean
4141
isStartedFetching: boolean
4242
isPreparingIndexing: boolean
43+
isIndexing: boolean
4344
didPassDiscord: boolean
4445
discordCodeInput: string
4546
docsLeftToIndex: number
4647
docsInIndexing: number
47-
lastServerDownTimestamp: number
48+
docsIndexed: number
49+
timeSinceLastIndexing: number
50+
serverDownCount: number
4851
showResultsPage: boolean
4952
}
5053

5154
export interface ServerStatus {
5255
docs_in_indexing: number
5356
docs_left_to_index: number
57+
docs_indexed: number
5458
}
5559

5660
Modal.setAppElement('#root');
@@ -103,8 +107,11 @@ export default class App extends React.Component <{}, AppState>{
103107
didPassDiscord: false,
104108
docsLeftToIndex: 0,
105109
docsInIndexing: 0,
110+
docsIndexed: 0,
111+
isIndexing: false,
112+
serverDownCount: 0,
113+
timeSinceLastIndexing: 0,
106114
searchDuration: 0,
107-
lastServerDownTimestamp: 0,
108115
showResultsPage: false
109116
}
110117

@@ -164,11 +171,9 @@ export default class App extends React.Component <{}, AppState>{
164171
}
165172

166173
async fetchStatsusForever() {
167-
let successSleepSeconds = 1;
168174
let timeBetweenFailToast = 5;
169175
let failSleepSeconds = 1;
170-
171-
api.get<ServerStatus>('/status').then((res) => {
176+
api.get<ServerStatus>('/status', {timeout: 3000}).then((res) => {
172177
if (this.state.isServerDown) {
173178
toast.dismiss();
174179
if (!document.hidden) {
@@ -178,33 +183,37 @@ export default class App extends React.Component <{}, AppState>{
178183
}
179184

180185
let isPreparingIndexing = this.state.isPreparingIndexing;
181-
if (this.state.isPreparingIndexing && (res.data.docs_in_indexing > 0 || res.data.docs_left_to_index > 0)) {
186+
let isIndexing = this.state.isIndexing;
187+
let lastIndexingTime = this.state.timeSinceLastIndexing;
188+
if (res.data.docs_in_indexing > 0 || res.data.docs_left_to_index > 0 || (res.data.docs_indexed > this.state.docsIndexed && this.state.docsIndexed > 0)) {
189+
isIndexing = true;
190+
lastIndexingTime = Date.now();
182191
isPreparingIndexing = false;
183-
}
184-
185-
if(this.state.docsInIndexing > 0 && (res.data.docs_in_indexing === 0 && res.data.docs_left_to_index === 0)) {
192+
} else if (isIndexing && Date.now() - lastIndexingTime > (1000 * 60 * 1)) {
193+
isIndexing = false;
186194
toast.success("Indexing finished.", {autoClose: 2000});
187195
}
188196

189197
this.setState({isServerDown: false, docsLeftToIndex: res.data.docs_left_to_index,
190-
docsInIndexing: res.data.docs_in_indexing, isPreparingIndexing: isPreparingIndexing});
198+
docsInIndexing: res.data.docs_in_indexing, isPreparingIndexing: isPreparingIndexing,
199+
docsIndexed: res.data.docs_indexed, isIndexing: isIndexing, timeSinceLastIndexing: lastIndexingTime});
191200

192-
let timeToSleep = isPreparingIndexing ? 1000 : successSleepSeconds * 1000;
201+
let timeToSleep = 1000;
193202
setTimeout(() => this.fetchStatsusForever(), timeToSleep);
194203
}).catch((err) => {
195-
this.setState({isServerDown: true});
204+
this.setState({serverDownCount: this.state.serverDownCount + 1});
196205

197-
if (Date.now() - this.state.lastServerDownTimestamp > 6000 && !document.hidden) { // if it's 6 seconds since last server down, show a toast
206+
if (this.state.serverDownCount > 5 && !document.hidden) { // if it's 6 seconds since last server down, show a toast
198207
toast.dismiss();
199-
toast.error(`Server is down, retrying in ${timeBetweenFailToast} seconds...`, {autoClose: (timeBetweenFailToast-1) * 1000});
200-
this.setState({lastServerDownTimestamp: Date.now()});
208+
toast.error(`Server is not responding (retrying...)`, {autoClose: (timeBetweenFailToast-1) * 1000});
209+
this.setState({isServerDown: true, serverDownCount: 0});
201210
}
202211
setTimeout(() => this.fetchStatsusForever(), failSleepSeconds * 1000);
203212
})
204213
}
205214

206215
inIndexing() {
207-
return this.state.isPreparingIndexing || this.state.docsInIndexing > 0 || this.state.docsLeftToIndex > 0;
216+
return this.state.isPreparingIndexing || this.state.isIndexing;
208217
}
209218

210219
getIndexingStatusText() {
@@ -213,17 +222,36 @@ export default class App extends React.Component <{}, AppState>{
213222
}
214223

215224
if (this.state.docsInIndexing > 0) {
216-
let text = "Indexing " + this.state.docsInIndexing + " documents... it might take a while.";
225+
let text = "Indexing " + this.state.docsInIndexing + " documents...";
217226
if (this.state.docsLeftToIndex > 0) {
218-
text += " (" + this.state.docsLeftToIndex + "~ left in queue)";
227+
text += " (" + this.state.docsLeftToIndex + " left in queue";
228+
if (this.state.docsIndexed > 0) {
229+
text += ", " + this.state.docsIndexed + " documents are indexed & searchable)";
230+
} else {
231+
text += ")";
232+
}
233+
234+
} else {
235+
if (this.state.docsIndexed > 0) {
236+
text += " (" + this.state.docsIndexed + " documents are indexed & searchable)";
237+
}
219238
}
220239

221240
return text;
222241
}
223242

224243
if (this.state.docsLeftToIndex > 0) {
225-
return "Preparing to index...";
244+
let text = "Preparing to index";
245+
if (this.state.docsIndexed > 0) {
246+
text += " (" + this.state.docsIndexed + " documents are indexed & searchable)";
247+
} else {
248+
text += "...";
249+
}
250+
return text;
226251
}
252+
253+
return `Indexing... (${this.state.docsIndexed} documents are indexed & searchable)`;
254+
227255
}
228256

229257
openModal() {

ui/src/components/search-result.tsx

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,14 @@ export const SearchResult = (props: SearchResultProps) => {
8787
<span className='flex flex-row items-center justify-center ml-2 mt-[5px]'>
8888
Commented {getDaysAgo(props.resultDetails.time)}
8989
</span>
90-
)
91-
90+
)
91+
}
92+
{
93+
props.resultDetails.type === ResultType.Message && (
94+
<span className='flex flex-row items-center justify-center ml-2 mt-[5px]'>
95+
Sent {getDaysAgo(props.resultDetails.time)}
96+
</span>
97+
)
9298
}
9399
{props.resultDetails.type === ResultType.Issue &&
94100
<span className='flex flex-row items-center ml-2 px-[7px] py-[1px] font-poppins font-medium text-[15px] bg-[#392E58] text-[#8F76C6] rounded-lg'>
@@ -120,12 +126,15 @@ export const SearchResult = (props: SearchResultProps) => {
120126
{props.resultDetails.type === ResultType.Message && '#'}
121127
{props.resultDetails.location} </span>
122128
</span>
123-
<span className="ml-1 flex flex-row items-center">
124-
<Img alt="author" className="inline-block ml-[6px] mr-2 h-4 rounded-xl"
125-
src={[props.resultDetails.author_image_url, props.resultDetails.author_image_data, DefaultUserImage]}></Img>
126-
<span className='capitalize'>{props.resultDetails.author} </span>
127-
</span>
128-
{props.resultDetails.child === null && DateSpan(props)}
129+
{
130+
props.resultDetails.type !== ResultType.Message &&
131+
<span className="ml-1 flex flex-row items-center">
132+
<Img alt="author" className="inline-block ml-[6px] mr-2 h-4 rounded-xl"
133+
src={[props.resultDetails.author_image_url, props.resultDetails.author_image_data, DefaultUserImage]}></Img>
134+
<span className='capitalize'>{props.resultDetails.author} </span>
135+
</span>
136+
}
137+
{props.resultDetails.child === null && props.resultDetails.type !== ResultType.Message && DateSpan(props)}
129138
<span className="flex flex-row items-center">
130139
&thinsp; |&thinsp;
131140
<img alt="file-type" className="inline ml-2 mx-1 h-[12px] w-[12px] grayscale-[0.55]"

0 commit comments

Comments
 (0)