#StandWithUkraine

Update FeedDemon Watches with AutoIt

AutoIt FeedDemon RSS reader among others things has Watches functions. Instead of specific RSS it searches feeds for keywords and combines found items together into mashup.

It works very well except that manual input of keywords is too troublesome to create complex Watches. I wanted to create Watch integrated with Appnews internals and started to look for external keywords edit.

feeddemon_watches

feeddemon_watches

Watch format

I remembered that when setting up new backup routine for Cobian Backup (inspired by my computer meltdown, yeah I am still talking about that) I saw Watches saved with rest of FeedDemon settings in:

C:\Documents and Settings\[profile]\Local Settings\Application Data
\FeedDemon\v1\Watches\

That folder has bunch of files with really cryptic names and .RSSW extensions. Inside each file is XML that holds all setting for a watch, including set of keywords:

Something like this:

<watch xmlns:fd="http://www.bradsoft.com/feeddemon/xmlns/1.0/"
version="1.6" fd:feedId="D40C7E27-19C1-45FF-AE7A-3B85054531BF"
fd:autoPurgeEnabled="true" fd:autoPurgeMaxItems="50">
<em><title>Appnews</title><</em>fd:state
fd:numUnread="0" fd:numFlagged="0" fd:numTotal="50"/>
<location title="true" description="false"/><options wholeWords="true"
matchCase="false" matchAllWords="false" markReadInSourceFeed="false"/>
<em><keyword>Appnews</keyword></</em>watch>

So after creating Watch in FeedDemon I can find it by </em> and add all <em><keyword></em> I need.</p> <h3 id="autoit">AutoIt</h3> <p>As always <a href="https://www.rarst.net/tag/autoit/">AutoIt</a> is my first choice for simple programming tasks. I decided to make a function that will:</p> <ol> <li>Find specific Watch file by Watch name.</li> <li>Change keywords in Watch as I need.</li> </ol> <h3 id="step-by-step">Step by step</h3> <p><strong>Start</strong>. Array include is needed for some functions down the road. Function declaration - takes two parameters – name of Watch to edit and array of keywords.</p> <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>#include <Array.au3> </span></span><span style="display:flex;"><span>Func UpdateWatch($title,$array) </span></span></code></pre></div><p>Set up <strong>Watches directory</strong> and stops if not found. Uses <em>@UserProfileDir</em> macros to include profile of user in path.</p> <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>$path=@UserProfileDir&"\Local Settings\Application Data </span></span><span style="display:flex;"><span>\FeedDemon\v1\Watches\" </span></span><span style="display:flex;"><span>If Not FileExists($path) Then </span></span><span style="display:flex;"><span> MsgBox(0,"","Watches directory not found") </span></span><span style="display:flex;"><span> Return </span></span><span style="display:flex;"><span>EndIf </span></span></code></pre></div><p><strong>Set up search</strong> for specific Watch. Little tricky to understand but that’s how general file search mechanics in Windows go. Change directory to Watches, try to look for files that fit <em>*.rssw</em> format, stop if not found.</p> <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>FileChangeDir($path) </span></span><span style="display:flex;"><span>$search=FileFindFirstFile("*.rssw") </span></span><span style="display:flex;"><span>If $search=-1 Then </span></span><span style="display:flex;"><span> MsgBox(0,"","Search error") </span></span><span style="display:flex;"><span>Return </span></span><span style="display:flex;"><span>EndIf </span></span></code></pre></div><p><strong>Actual search</strong>. We are going through all files, looking for the one that has title equal to what was passed to function. If nothing is found we break search and stop function.</p> <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>$watch=False </span></span><span style="display:flex;"><span>$pattern="<title>"&$title&"</title>" </span></span><span style="display:flex;"><span>While Not $watch </span></span><span style="display:flex;"><span> $file = FileFindNextFile($search) </span></span><span style="display:flex;"><span> If @error Then ExitLoop </span></span><span style="display:flex;"><span> If StringInStr(FileRead($file),$pattern) Then $watch=$file </span></span><span style="display:flex;"><span>WEnd </span></span><span style="display:flex;"><span>FileClose($search) </span></span><span style="display:flex;"><span>If Not $watch Then </span></span><span style="display:flex;"><span> MsgBox(0,"","Watch not found") </span></span><span style="display:flex;"><span> Return </span></span><span style="display:flex;"><span>EndIf </span></span></code></pre></div><p><strong>Keywords array preparation</strong>. For each item in array passed to function content is enclosed in keyword tags.</p> <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>$s=UBound($array)-1 </span></span><span style="display:flex;"><span>For $i=0 To $s </span></span><span style="display:flex;"><span> $array[$i]="<keyword>"&$array[$i]&"</keyword>" </span></span><span style="display:flex;"><span>Next </span></span></code></pre></div><p><strong>File edit</strong>. Read Watch file. Replace everything from start of keywords to the end with keywords from array. Write file back (with creating backup copy to be safe).</p> <div class="highlight"><pre tabindex="0" style="color:#93a1a1;background-color:#002b36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;"><code class="language-fallback" data-lang="fallback"><span style="display:flex;"><span>$xml=FileRead($watch) </span></span><span style="display:flex;"><span>FileCopy($watch,$watch&".bak") </span></span><span style="display:flex;"><span>$pattern="(<keyword>.*)" </span></span><span style="display:flex;"><span>$replace=_ArrayToString($array,"")&"</watch>" </span></span><span style="display:flex;"><span>$xml=StringRegExpReplace($xml,$pattern,$replace) </span></span><span style="display:flex;"><span>FileDelete($watch) </span></span><span style="display:flex;"><span>FileWrite($watch,$xml) </span></span><span style="display:flex;"><span>EndFunc </span></span></code></pre></div><p>And all together:</p> <p><strong>Script</strong> <a href="https://www.rarst.net/script/feed_demon_updatewatch.au3">https://www.rarst.net/script/feed_demon_updatewatch.au3</a></p> <p>For results to show up in FeedDemon it must be restarted after running function.</p> <h3 id="overall">Overall</h3> <p>So instead of tedious manual input now I just feed list of program titles from <a href="http://appnews.net/">Appnews</a> managing software into Watch. Ensures that I don’t miss anything and (at times) makes me feel great when I beat software portals at update speed. :)</p> <p>What would you track with large list of keywords?</p> <h3>Related Posts</h3> <ul> <li><a href="https://www.rarst.net/software/rss-productivity/">No way out increases RSS productivity</a></li> <li><a href="https://www.rarst.net/software/newsgator/">NewsGator family of RSS readers</a></li> <li><a href="https://www.rarst.net/web/dead-rss-links-opml/">Weeding dead RSS feeds with processing OPML</a></li> <li><a href="https://www.rarst.net/software/freeware-end/">When freeware runs out of free</a></li> <li><a href="https://www.rarst.net/thoughts/1000-subscribers-milestone/">1000 subscribers milestone</a></li> </ul> </div> <nav> <ul class="pager"> <li class="previous"> <a href="https://www.rarst.net/code/cureit-updater/">« Simplify CureIt update process with AutoIt</a> </li> <li class="next"> <a href="https://www.rarst.net/software/movefile/">How to schedule file move or delete on reboot »</a> </li> </ul> </nav> </article> <section id="comments-template" class="row"> <div class="col-md-12"> <h3 id="comments-number">1 Comments</h3> </div> <div class="col-sm-11 col-md-10"> <ul class="comment-list media-list"> <li class="comment clearfix"> <article> <div class="pull-left"> <img alt="" src="https://secure.gravatar.com/avatar/d41d8cd98f00b204e9800998ecf8427e?s=165&d=identicon&r=g" srcset="https://secure.gravatar.com/avatar/d41d8cd98f00b204e9800998ecf8427e?s=330&d=identicon&r=g 2x" class="avatar avatar-165 photo" height="165" width="165" loading="lazy"/> </div> <div class="media-body"> <h4 class="media-heading comment-meta text-left"> <cite>FeedDemon gets native tool to filter feed content | Rarst.net</cite> <span class="pull-right text-muted"> <time datetime="2010-01-23T23:22:14+02:00">2010-01-23</time> <a title="Permalink" href="#comment-14267">#</a> </span> </h4> <div>[...] Filters are very similar to Watches, except they don’t create separate item in feeds tree and are applied to specified feeds [...]</div> </div> </article> </li> </ul> </div> </section> </main> </div> <footer id="footer" class="row"> <div class="col-sm-12"> <hr/> </div> <div class="col-sm-12"> <p class="credit"> © 2008–2022 <a href="https://www.rarst.net/" title="Home page">Rarst.net</a> </p> </div> </footer> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/8.9.1/styles/solarized_dark.min.css" integrity="sha256-Si/lZpzlGW0CtfqnOyIiWjhYoeMUIui7MyR0TxlnHss=" crossorigin="anonymous" /> <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/8.9.1/highlight.min.js" integrity="sha256-WtUXEofG2M0/YE3zVZEpwoxarqbMZ8ze89ClCdvdemQ=" crossorigin="anonymous"></script> <script type="text/javascript"> document.addEventListener("DOMContentLoaded", function () { document.querySelectorAll('pre').forEach(function (item) { hljs.highlightBlock(item) }); }); </script> </div> </body> </html>