diff --git a/CHANGELOG.md b/CHANGELOG.md
index 896c593..f4ee573 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,9 +1,14 @@
 # Changelog
 
 ## 1.18.0
-* Upgraded to Electron 18
+* Upgraded to Electron 17
 * Fixed MacOS M1 incompatibilities
 * Improved age detection
+* Taur and feral body types are now matched against kink preferences
+* Filtered messages are now accessible in the conversation history
+* Rejection messages are now also sent to filtered characters whose profiles have not been scored at the time they message you
+* Slightly relaxed filter scoring
+* Character preview now shows last messages from conversation history 
 
 ## 1.17.1
 * Fixes to smart filters
diff --git a/README.md b/README.md
index d1b11a7..e80d335 100644
--- a/README.md
+++ b/README.md
@@ -1,8 +1,8 @@
 # Download
-[Windows](https://github.com/mrstallion/fchat-rising/releases/download/v1.17.1/F-Chat-Rising-1.17.1-win.exe) (82 MB)
-| [MacOS Intel](https://github.com/mrstallion/fchat-rising/releases/download/v1.17.1/F-Chat-Rising-1.17.1-macos-intel.dmg) (82 MB)
-| [MacOS M1](https://github.com/mrstallion/fchat-rising/releases/download/v1.17.1/F-Chat-Rising-1.17.1-macos-m1.dmg) (84 MB)
-| [Linux](https://github.com/mrstallion/fchat-rising/releases/download/v1.17.1/F-Chat-Rising-1.17.1-linux.AppImage) (82 MB)
+[Windows](https://github.com/mrstallion/fchat-rising/releases/download/v1.18.0/F-Chat-Rising-1.18.0-win.exe) (82 MB)
+| [MacOS Intel](https://github.com/mrstallion/fchat-rising/releases/download/v1.18.0/F-Chat-Rising-1.18.0-macos-intel.dmg) (82 MB)
+| [MacOS M1](https://github.com/mrstallion/fchat-rising/releases/download/v1.18.0/F-Chat-Rising-1.18.0-macos-m1.dmg) (84 MB)
+| [Linux](https://github.com/mrstallion/fchat-rising/releases/download/v1.18.0/F-Chat-Rising-1.18.0-linux.AppImage) (82 MB)
 
 
 # F-Chat Rising
diff --git a/chat/conversations.ts b/chat/conversations.ts
index 4234e17..c3a0197 100644
--- a/chat/conversations.ts
+++ b/chat/conversations.ts
@@ -618,8 +618,23 @@ function isOfInterest(this: any, character: Character): boolean {
     return character.isFriend || character.isBookmarked || state.privateMap[character.name.toLowerCase()] !== undefined;
 }
 
-async function testSmartFilterForPrivateMessage(fromChar: Character.Character): Promise<boolean> {
+async function withNeutralVisibilityPrivateConversation(
+    character: Character.Character,
+    cb: (p: PrivateConversation, c: Character.Character) => Promise<void>
+): Promise<void> {
+    const isVisibleConversation = !!(state.getPrivate as any)(character, true);
+    const conv = state.getPrivate(character);
+
+    await cb(conv, character);
+
+    if (!isVisibleConversation) {
+        await conv.close();
+    }
+}
+
+export async function testSmartFilterForPrivateMessage(fromChar: Character.Character, originalMessage?: Message): Promise<boolean> {
     const cachedProfile = core.cache.profileCache.getSync(fromChar.name) || await core.cache.profileCache.get(fromChar.name);
+    const firstTime = cachedProfile && !cachedProfile.match.autoResponded;
 
     if (
         cachedProfile &&
@@ -629,23 +644,51 @@ async function testSmartFilterForPrivateMessage(fromChar: Character.Character):
     ) {
         cachedProfile.match.autoResponded = true;
 
-        log.debug('filter.autoresponse', { name: fromChar.name });
-
         void Conversation.conversationThroat(
           async() => {
+                log.debug('filter.autoresponse', { name: fromChar.name });
+
                 await Conversation.testPostDelay();
 
-              // tslint:disable-next-line:prefer-template
-                const m = '[Automated message] Sorry, the player of this character has indicated that they are not interested in characters matching your profile. They will not see your message.\n\n' +
-                    'Need a filter for yourself? Try out [url=https://mrstallion.github.io/fchat-rising/]F-Chat Rising[/url]';
+                // tslint:disable-next-line:prefer-template
+                const message = {
+                    recipient: fromChar.name,
+                    message: '\n[sub][color=orange][b][AUTOMATED MESSAGE][/b][/color][/sub]\n' +
+                      'Sorry, the player of this character is not interested in characters matching your profile.' +
+                      `${core.state.settings.risingFilter.hidePrivateMessages ? ' They did not see your message. To bypass this warning, send your message again.' : ''}\n` +
+                      '\n' +
+                      '🦄 Need a filter for yourself? Try out [url=https://mrstallion.github.io/fchat-rising/]F-Chat Rising[/url]'
+                };
 
-                core.connection.send('PRI', {recipient: fromChar.name, message: m});
+                core.connection.send('PRI', message);
                 core.cache.markLastPostTime();
+
+                if (core.state.settings.logMessages) {
+                    const logMessage = createMessage(Interfaces.Message.Type.Message, core.characters.ownCharacter,
+                        message.message, new Date());
+
+                    await withNeutralVisibilityPrivateConversation(
+                      fromChar,
+                      async(p) => core.logs.logMessage(p, logMessage)
+                    );
+                }
               }
         );
     }
 
-    if (cachedProfile && cachedProfile.match.isFiltered && core.state.settings.risingFilter.hidePrivateMessages) {
+    if (
+        cachedProfile &&
+        cachedProfile.match.isFiltered &&
+        core.state.settings.risingFilter.hidePrivateMessages &&
+        firstTime // subsequent messages bypass this filter on purpose
+    ) {
+        if (core.state.settings.logMessages && originalMessage) {
+            await withNeutralVisibilityPrivateConversation(
+              fromChar,
+              async(p) => core.logs.logMessage(p, originalMessage)
+            );
+        }
+
         return true;
     }
 
@@ -738,7 +781,7 @@ export default function(this: any): Interfaces.State {
         if(char.isIgnored) return connection.send('IGN', {action: 'notify', character: data.character});
         const message = createMessage(MessageType.Message, char, decodeHTML(data.message), time);
 
-        if (await testSmartFilterForPrivateMessage(char) === true) {
+        if (await testSmartFilterForPrivateMessage(char, message) === true) {
             return;
         }
 
diff --git a/chat/interfaces.ts b/chat/interfaces.ts
index 9bbe2c1..05714a9 100644
--- a/chat/interfaces.ts
+++ b/chat/interfaces.ts
@@ -92,7 +92,9 @@ export namespace Conversation {
         readonly selectedConversation: Conversation
         readonly hasNew: boolean;
         byKey(key: string): Conversation | undefined
-        getPrivate(character: Character): PrivateConversation
+
+        getPrivate(character: Character): PrivateConversation;
+        getPrivate(character: Character, noCreate: boolean): PrivateConversation | undefined;
     }
 
     export enum Setting {
diff --git a/chat/preview/CharacterPreview.vue b/chat/preview/CharacterPreview.vue
index 7949e8e..75c83c3 100644
--- a/chat/preview/CharacterPreview.vue
+++ b/chat/preview/CharacterPreview.vue
@@ -43,6 +43,15 @@
           <bbcode :text="statusMessage"></bbcode>
         </div>
 
+        <div class="conversation" v-if="conversation && conversation.length > 0">
+          <h4>Latest Messages</h4>
+
+          <template v-for="message in conversation">
+              <message-view :message="message" :key="message.id">
+              </message-view>
+          </template>
+        </div>
+
         <div class="latest-ad-message" v-if="latestAd && (latestAd.message !== statusMessage)">
           <h4>Latest Ad <span class="message-time">{{formatTime(latestAd.datePosted)}}</span></h4>
           <bbcode :text="latestAd.message"></bbcode>
@@ -82,6 +91,8 @@ import { EventBus } from './event-bus';
 import { Character, CustomKink } from '../../interfaces';
 import { matchesSmartFilters, testSmartFilters } from '../../learn/filter/smart-filter';
 import { smartFilterTypes } from '../../learn/filter/types';
+import { Conversation } from '../interfaces';
+import MessageView from '../message_view';
 
 interface CustomKinkWithScore extends CustomKink {
   score: number;
@@ -91,7 +102,8 @@ interface CustomKinkWithScore extends CustomKink {
 @Component({
     components: {
       'match-tags': MatchTags,
-      bbcode: BBCodeView(core.bbCodeParser)
+      bbcode: BBCodeView(core.bbCodeParser),
+      'message-view': MessageView
     }
 })
 export default class CharacterPreview extends Vue {
@@ -132,6 +144,8 @@ export default class CharacterPreview extends Vue {
   scoreWatcher: ((event: any) => void) | null = null;
   customs?: CustomKinkWithScore[];
 
+  conversation?: Conversation.Message[];
+
 
   @Hook('mounted')
   mounted(): void {
@@ -189,6 +203,8 @@ export default class CharacterPreview extends Vue {
     this.customs = undefined;
     this.ownCharacter = core.characters.ownProfile;
 
+    this.conversation = undefined;
+
     this.smartFilterIsFiltered = false;
     this.smartFilterDetails = [];
 
@@ -199,6 +215,8 @@ export default class CharacterPreview extends Vue {
       this.character = await this.getCharacterData(characterName);
       this.match = Matcher.identifyBestMatchReport(this.ownCharacter!.character, this.character!.character);
 
+      void this.updateConversationStatus();
+
       this.updateSmartFilterReport();
       this.updateCustoms();
       this.updateDetails();
@@ -229,6 +247,25 @@ export default class CharacterPreview extends Vue {
       ];
   }
 
+  async updateConversationStatus(): Promise<void> {
+    const char = core.characters.get(this.characterName!);
+
+    if (char) {
+      const messages = await core.logs.getLogs(core.characters.ownCharacter.name, char.name.toLowerCase(), new Date());
+      const matcher = /\[AUTOMATED MESSAGE]/;
+
+      this.conversation = _.map(
+        _.takeRight(_.filter(messages, (m) => !matcher.exec(m.text)), 3),
+          (m) => ({
+            ...m,
+            text: m.text.length > 512 ? m.text.substr(0, 512) + '…' : m.text
+          })
+      );
+
+      // this.conversation = core.conversations.getPrivate(char, true);
+    }
+  }
+
   updateOnlineStatus(): void {
     this.onlineCharacter = core.characters.get(this.characterName!);
 
@@ -432,6 +469,7 @@ export default class CharacterPreview extends Vue {
 
     .status-message,
     .latest-ad-message,
+    .conversation,
     .filter-matches {
       display: block;
       background-color: rgba(0,0,0,0.2);
diff --git a/docs/_config.yml b/docs/_config.yml
index 3a2b271..5d71a41 100644
--- a/docs/_config.yml
+++ b/docs/_config.yml
@@ -50,7 +50,7 @@ theme: jekyll-theme-slate
 changelog: https://github.com/mrstallion/fchat-rising/blob/master/CHANGELOG.md
 
 download:
-  version: 1.17.1
+  version: 1.18.0
 
   url: https://github.com/mrstallion/fchat-rising/releases/download/v%VERSION%/F-Chat-Rising-%VERSION%-%PLATFORM_TAIL%
 
diff --git a/electron/package.json b/electron/package.json
index 8dd64f0..c9811d6 100644
--- a/electron/package.json
+++ b/electron/package.json
@@ -1,6 +1,6 @@
 {
   "name": "fchat",
-  "version": "1.17.1",
+  "version": "1.18.0",
   "author": "The F-List Team and Mister Stallion (Esq.)",
   "description": "F-List.net Chat Client",
   "main": "main.js",
diff --git a/learn/cache-manager.ts b/learn/cache-manager.ts
index 7304a7c..8ec5724 100644
--- a/learn/cache-manager.ts
+++ b/learn/cache-manager.ts
@@ -21,7 +21,8 @@ import { PermanentIndexedStore } from './store/types';
 import * as path from 'path';
 // import * as electron from 'electron';
 
-import log from 'electron-log'; //tslint:disable-line:match-default-export-name
+import log from 'electron-log';
+import { testSmartFilterForPrivateMessage } from '../chat/conversations'; //tslint:disable-line:match-default-export-name
 
 
 export interface ProfileCacheQueueEntry {
@@ -132,8 +133,21 @@ export class CacheManager {
         );
 
         this.populateAllConversationsWithScore(c.character.name, score, isFiltered);
+        void this.respondToPendingRejections(c);
     }
 
+    // Manage rejections in case we didn't have a score at the time we received the message
+    async respondToPendingRejections(c: ComplexCharacter): Promise<void> {
+      const char = core.characters.get(c.character.name);
+
+      if (char && char.status !== 'offline') {
+        const conv = core.conversations.getPrivate(char, true);
+
+        if (conv && conv.messages.length > 0 && Date.now() - _.last(conv.messages)!.time.getTime() < 3 * 60 * 1000) {
+          await testSmartFilterForPrivateMessage(char);
+        }
+      }
+    }
 
     async addProfile(character: string | ComplexCharacter): Promise<void> {
         if (typeof character === 'string') {
diff --git a/learn/filter/smart-filter.ts b/learn/filter/smart-filter.ts
index 569a4fa..e853e0f 100644
--- a/learn/filter/smart-filter.ts
+++ b/learn/filter/smart-filter.ts
@@ -1,4 +1,4 @@
-import _ from 'lodash';
+import * as _ from 'lodash';
 import { Matcher } from '../matcher';
 import { BodyType, Build, Gender, Kink, Species, TagId } from '../matcher-types';
 import { SmartFilterSelection, SmartFilterSettings } from './types';
@@ -28,6 +28,10 @@ export interface SmartFilterTestResult {
   kinks: boolean;
 }
 
+function getBaseLog(base: number, x: number): number {
+  return Math.log(x) / Math.log(base);
+}
+
 export class SmartFilter {
   constructor(private opts: SmartFilterOpts) {}
 
@@ -64,7 +68,10 @@ export class SmartFilter {
       return curScore;
     }, { score: 0, matches: 0 });
 
-    return score.matches >= 1 && score.score >= 1.0 + (Math.log((this.opts.kinks?.length || 0) + 1) / 2);
+    const baseLog = getBaseLog(5, (this.opts.kinks?.length || 0) + 1);
+    const threshold = (baseLog * baseLog) + 1;
+
+    return score.matches >= 1 && score.score >= threshold;
   }
 
   testBuilds(c: Character): boolean {
diff --git a/learn/matcher-types.ts b/learn/matcher-types.ts
index 87fc0cb..3ddc17e 100644
--- a/learn/matcher-types.ts
+++ b/learn/matcher-types.ts
@@ -241,7 +241,9 @@ export enum Kink {
     Microphilia = 286,
     SizeDifferencesMicroMacro = 502,
     GrowthMacro = 384,
-    ShrinkingMicro = 387
+    ShrinkingMicro = 387,
+
+    Taurs = 68
 }
 
 export enum FurryPreference {
@@ -341,6 +343,16 @@ export const genderKinkMapping: GenderKinkIdMap = {
     [Gender.Transgender]: Kink.Transgenders
 };
 
+
+export interface BodyTypeKinkIdMap {
+    [key: number]: Kink
+}
+
+export const bodyTypeKinkMapping: BodyTypeKinkIdMap = {
+    [BodyType.Feral]: Kink.AnimalsFerals,
+    [BodyType.Taur]: Kink.Taurs
+};
+
  // if no species and 'no furry characters', === human
  // if no species and dislike 'anthro characters' === human
 
@@ -468,7 +480,7 @@ export const speciesMapping: SpeciesMap = {
         ],
 
     [Species.Human]: ['human', 'homo sapiens', 'human.*', 'homo[ -]?sapi[ea]ns?', 'woman', 'hy?[uo]+m[aie]n', 'humaine?',
-        'meat[ -]?popsicle',
+        'meat[ -]?popsicle'
         ],
 
     [Species.Elf]: ['drow', 'draenei', 'dunmer', 'draenai', 'blutelf[e]?', 'elf.*', 'drow.*', 'e[ -]l[ -]f', 'sin\'?dorei',
diff --git a/learn/matcher.ts b/learn/matcher.ts
index fb4be9e..9543a44 100644
--- a/learn/matcher.ts
+++ b/learn/matcher.ts
@@ -10,7 +10,7 @@ import anyAscii from 'any-ascii';
 import { Store } from '../site/character_page/data_store';
 
 import {
-    BodyType,
+    BodyType, bodyTypeKinkMapping,
     fchatGenderMap,
     FurryPreference,
     Gender,
@@ -151,6 +151,7 @@ export class CharacterAnalysis {
     readonly subDomRole: SubDomRole | null;
     readonly position: Position | null;
     readonly postLengthPreference: PostLengthPreference | null;
+    readonly bodyType: BodyType | null;
 
     readonly isAnthro: boolean | null;
     readonly isHuman: boolean | null;
@@ -166,10 +167,9 @@ export class CharacterAnalysis {
         this.subDomRole = Matcher.getTagValueList(TagId.SubDomRole, c);
         this.position = Matcher.getTagValueList(TagId.Position, c);
         this.postLengthPreference = Matcher.getTagValueList(TagId.PostLength, c);
+        this.bodyType = Matcher.getTagValueList(TagId.BodyType, c);
 
-        const ageTag = Matcher.getTagValue(TagId.Age, c);
-
-        this.age = ((ageTag) && (ageTag.string)) ? parseInt(ageTag.string, 10) : null;
+        this.age = Matcher.age(c);
 
         this.isAnthro = Matcher.isAnthro(c);
         this.isHuman = Matcher.isHuman(c);
@@ -405,7 +405,8 @@ export class Matcher {
                 [TagId.SubDomRole]: this.resolveSubDomScore(),
                 [TagId.Kinks]: this.resolveKinkScore(pronoun),
                 [TagId.PostLength]: this.resolvePostLengthScore(),
-                [TagId.Position]: this.resolvePositionScore()
+                [TagId.Position]: this.resolvePositionScore(),
+                [TagId.BodyType]: this.resolveBodyTypeScore()
             },
 
             info: {
@@ -723,6 +724,19 @@ export class Matcher {
         return new Score(Scoring.NEUTRAL);
     }
 
+    private resolveBodyTypeScore(): Score {
+        const theirBodyType = Matcher.getTagValueList(TagId.BodyType, this.them);
+
+        if (theirBodyType && theirBodyType in bodyTypeKinkMapping) {
+            const bodyTypePreference = Matcher.getKinkPreference(this.you, bodyTypeKinkMapping[theirBodyType]);
+
+            if (bodyTypePreference !== null) {
+                return Matcher.formatKinkScore(bodyTypePreference, `{BodyType[theirBodyType].toLowerCase()}s`);
+            }
+        }
+
+        return new Score(Scoring.NEUTRAL);
+    }
 
     private resolveSubDomScore(): Score {
         const you = this.you;
@@ -954,7 +968,6 @@ export class Matcher {
         return result;
     }
 
-
     // private countKinksByBucket(kinks: { [key: number]: KinkChoice }): { favorite: number, yes: number, maybe: number, no: number } {
     //     return _.reduce(
     //       kinks,
@@ -971,7 +984,6 @@ export class Matcher {
     //     );
     // }
 
-
     private getAllStandardKinks(c: Character): { [key: number]: KinkChoice } {
         const kinks = _.pickBy(c.kinks, _.isString);
 
@@ -1244,7 +1256,8 @@ export class Matcher {
 
         const ageStr = rawAge.string.toLowerCase().replace(/[,.]/g, '').trim();
 
-        if ((ageStr.indexOf('shota') >= 0) || (ageStr.indexOf('loli') >= 0) || (ageStr.indexOf('lolli') >= 0) || (ageStr.indexOf('pup') >= 0)) {
+        if ((ageStr.indexOf('shota') >= 0) || (ageStr.indexOf('loli') >= 0)
+            || (ageStr.indexOf('lolli') >= 0) || (ageStr.indexOf('pup') >= 0)) {
             return 10;
         }
 
@@ -1297,7 +1310,8 @@ export class Matcher {
             return { min: Math.min(v1, v2), max: Math.max(v1, v2) };
         }
 
-        if ((ageStr.indexOf('shota') >= 0) || (ageStr.indexOf('loli') >= 0) || (ageStr.indexOf('lolli') >= 0) || (ageStr.indexOf('pup') >= 0)) {
+        if ((ageStr.indexOf('shota') >= 0) || (ageStr.indexOf('loli') >= 0)
+            || (ageStr.indexOf('lolli') >= 0) || (ageStr.indexOf('pup') >= 0)) {
             return { min: 10, max: 10 };
         }
 
diff --git a/package.json b/package.json
index 2b791c0..4597155 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "f-list-rising",
-  "version": "1.17.1",
+  "version": "1.18.0",
   "author": "The F-List Team and and Mister Stallion (Esq.)",
   "description": "A heavily modded F-Chat 3.0 client for F-List",
   "license": "MIT",
diff --git a/site/character_page/infotag.vue b/site/character_page/infotag.vue
index 352c4de..9fc42c7 100644
--- a/site/character_page/infotag.vue
+++ b/site/character_page/infotag.vue
@@ -63,7 +63,7 @@
         }
 
         yourInterestIsRelevant(id: number): boolean {
-            return ((id === TagId.Gender) || (id === TagId.Age) || (id === TagId.Species));
+            return ((id === TagId.Gender) || (id === TagId.Age) || (id === TagId.Species) || (id === TagId.BodyType));
         }
 
         get contactLink(): string | undefined {