diff --git a/src/debug.py b/src/debug.py
index d2f9107..18a64f7 100644
--- a/src/debug.py
+++ b/src/debug.py
@@ -78,4 +78,4 @@ def setup(bot):
         if ext == "all":
             for ext in util.extensions: bot.reload_extension(ext)
         else: bot.reload_extension(ext)
-        await ctx.send("Done!")
\ No newline at end of file
+        await ctx.send("Done!")
diff --git a/src/telephone.py b/src/telephone.py
index c72ee18..f6282f3 100644
--- a/src/telephone.py
+++ b/src/telephone.py
@@ -4,10 +4,12 @@ import logging
 import asyncio
 import re
 import hashlib
-from datetime import datetime
+from datetime import datetime, timedelta
 import os
 import pydot
 import tempfile
+import collections
+import aiohttp
 
 import util
 import eventbus
@@ -179,6 +181,88 @@ When you want to end a call, use hangup.
         await ctx.send(f"Successfully deleted.")
         pass
 
+
+    async def _find_recent(self, chs, query):
+        one_week = timedelta(seconds=60*60*24*7)
+        one_week_ago = datetime.now()-one_week
+
+        found = collections.defaultdict(list)
+        async def find_msgs(ch):
+            # the parameters to history() here might need to be tweaked
+            # somewhat, for more general usage
+            async for msg in ch.history(limit=None,after=one_week_ago):
+                if query in msg.content.lower():
+                    found[ch].append(msg)
+        await asyncio.gather(*(find_msgs(ch) for ch in chs))
+        return found
+
+    @telephone.command(brief="find recent messages in channels linked to this")
+    @commands.check(util.extpriv_check)
+    async def searchrecent(self, ctx, ch: discord.TextChannel, *, query):
+        author = ctx.author
+        chs = []
+        for dest in eventbus.find_all_destinations(('discord',ch.id)):
+            if dest[0] == 'discord':
+                chs.append(self.bot.get_channel(dest[1]))
+
+        found = await self._find_recent(chs, query)
+
+        out = ""
+        for ch,ms in found.items():
+            out += f"{ch.mention} (`#{ch.name}` in `{ch.guild.name}`)\n"
+            for m in ms:
+                u = m.author.name if m.author else None
+                w = "[WH]" if m.webhook_id else ""
+                out += f"- {m.content[:20]} @{m.created_at} by {u} {w}\n"
+
+        for c in util.chunks(out,2000):
+            await author.send(c)
+
+        return found
+
+    @telephone.command(brief="delete recent messages in channels linked to this")
+    @commands.check(util.extpriv_check)
+    async def delrecent(self, ctx, ch: discord.TextChannel, *, query):
+        author = ctx.author
+        found = await self.searchrecent(ctx,ch,query=query)
+
+        await author.send("please say 'GO' to confirm or wait 10 seconds to not confirm")
+        try:
+            await self.bot.wait_for('message',check=lambda m:m.author == ctx.author and m.content == "GO" and m.channel == ctx.author.dm_channel,timeout=10)
+        except asyncio.TimeoutError:
+            await author.send("timed out")
+            return
+
+        async def try_delete(msg,session):
+            if msg.webhook_id is not None:
+                # note: assumes there is only one webhook we control per channel
+                # i think that's the case
+                wh_url = await self.bot.database.execute_fetchone("SELECT webhook FROM discord_webhooks WHERE channel_id = ?",(msg.channel.id,))
+                if wh_url is None:
+                    await author.send(f"no access to webhook: {msg.id} {msg.channel.mention} {msg.jump_url}")
+                    return
+                wh_url = wh_url['webhook']
+
+                wh = discord.Webhook.from_url(wh_url,session=session)
+                await wh.delete_message(msg.id)
+
+            else:
+                try:
+                    await msg.delete()
+                except discord.errors.Forbidden:
+                    await author.send(f"!!! couldn't delete msg {msg.id} in {msg.channel.mention}")
+
+        msgs = []
+        for q in found.values():
+            msgs.extend(q)
+
+        async with aiohttp.ClientSession() as session:
+            await asyncio.gather(*(try_delete(msg,session) for msg in msgs))
+
+        await author.send("done")
+
+
+
     @telephone.command(brief="Generate a webhook")
     @commands.check(util.server_mod_check)
     async def init_webhook(self, ctx):
@@ -223,7 +307,7 @@ When you want to end a call, use hangup.
         if address == originating_address: return await ctx.send(embed=util.error_embed("A channel cannot dial itself. That means *you*, Gibson."))
         recv_info = await self.get_addr_config(address)
         if not recv_info: return await ctx.send(embed=util.error_embed("Destination address not found. Please check for typos and/or antimemes."))
-        
+
         current_call = await self.bot.database.execute_fetchone("SELECT * FROM calls WHERE from_id = ?", (originating_address,))
         if current_call: return await ctx.send(embed=util.error_embed(f"A call is already open (to {current_call['to_id']}) from this channel. Currently, only one outgoing call is permitted at a time."))
 
@@ -348,4 +432,4 @@ When you want to end a call, use hangup.
 def setup(bot):
     cog = Telephone(bot)
     bot.add_cog(cog)
-    asyncio.create_task(cog.initial_load_webhooks())
\ No newline at end of file
+    asyncio.create_task(cog.initial_load_webhooks())
diff --git a/src/util.py b/src/util.py
index b053b56..e311034 100644
--- a/src/util.py
+++ b/src/util.py
@@ -327,4 +327,9 @@ def random_id():
     second_time -= SIMPLEFLAKE_EPOCH
     millisecond_time = int(second_time * 1000)
     randomness = random.getrandbits(SIMPLEFLAKE_RANDOM_LENGTH)
-    return (millisecond_time << SIMPLEFLAKE_TIMESTAMP_SHIFT) + randomness
\ No newline at end of file
+    return (millisecond_time << SIMPLEFLAKE_TIMESTAMP_SHIFT) + randomness
+
+def chunks(source, length):
+    for i in range(0, len(source), length):
+        yield source[i : i+length]
+