diff --git a/gajim/common/modules/base.py b/gajim/common/modules/base.py
new file mode 100644
index 0000000000000000000000000000000000000000..60008c8afc13eee6245c72d16b039e527d056517
--- /dev/null
+++ b/gajim/common/modules/base.py
@@ -0,0 +1,59 @@
+# This file is part of Gajim.
+#
+# Gajim is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published
+# by the Free Software Foundation; version 3 only.
+#
+# Gajim is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Gajim.  If not, see <http://www.gnu.org/licenses/>.
+
+import logging
+from functools import partial
+from unittest.mock import Mock
+
+from gajim.common import app
+
+log = logging.getLogger('gajim.c.m.base')
+
+
+class BaseModule:
+
+    _nbxmpp_extends = ''
+    _nbxmpp_methods = []
+
+    def __init__(self, con):
+        self._con = con
+        self._account = con.name
+        self._nbxmpp_callbacks = {}
+        self.handlers = []
+
+    def __getattr__(self, key):
+        if key not in self._nbxmpp_methods:
+            raise AttributeError
+        if not app.account_is_connected(self._account):
+            log.warning('Account %s not connected, cant use %s',
+                        self._account, key)
+            return
+
+        module = self._con.connection.get_module(self._nbxmpp_extends)
+
+        return partial(getattr(module, key),
+                       callback=self._nbxmpp_callbacks.get(key))
+
+    def _nbxmpp(self, module_name=None):
+        if not app.account_is_connected(self._account):
+            log.warning('Account %s not connected, cant use nbxmpp method',
+                        self._account)
+            return Mock()
+
+        if module_name is None:
+            return self._con.connection
+        return self._con.connection.get_module(module_name)
+
+    def _register_callback(self, method, callback):
+        self._nbxmpp_callbacks[method] = callback
diff --git a/gajim/common/modules/blocking.py b/gajim/common/modules/blocking.py
index d0947e0a90d4362c0588216799459febb9f3479b..5438a08198af66cb740fa4e82ed6d6b812e3df05 100644
--- a/gajim/common/modules/blocking.py
+++ b/gajim/common/modules/blocking.py
@@ -15,30 +15,28 @@
 # XEP-0191: Blocking Command
 
 import logging
-from functools import wraps
 
 import nbxmpp
 
 from gajim.common import app
 from gajim.common.nec import NetworkEvent
 from gajim.common.nec import NetworkIncomingEvent
+from gajim.common.modules.base import BaseModule
 
 log = logging.getLogger('gajim.c.m.blocking')
 
 
-def ensure_online(func):
-    @wraps(func)
-    def func_wrapper(self, *args, **kwargs):
-        if not app.account_is_connected(self._account):
-            return
-        return func(self, *args, **kwargs)
-    return func_wrapper
+class Blocking(BaseModule):
 
+    _nbxmpp_extends = 'Blocking'
+    _nbxmpp_methods = [
+        'block',
+        'unblock',
+        'get_blocking_list',
+    ]
 
-class Blocking:
     def __init__(self, con):
-        self._con = con
-        self._account = con.name
+        BaseModule.__init__(self, con)
 
         self.blocked = []
 
@@ -46,22 +44,10 @@ class Blocking:
             ('iq', self._blocking_push_received, 'set', nbxmpp.NS_BLOCKING)
         ]
 
-        self.supported = False
-
-        self._nbmxpp_methods = [
-            'block',
-            'unblock',
-        ]
+        self._register_callback('get_blocking_list',
+                                self._blocking_list_received)
 
-    def __getattr__(self, key):
-        if key not in self._nbmxpp_methods:
-            raise AttributeError
-        if not app.account_is_connected(self._account):
-            log.warning('Account %s not connected, cant use %s',
-                        self._account, key)
-            return
-        module = self._con.connection.get_module('Blocking')
-        return getattr(module, key)
+        self.supported = False
 
     def pass_disco(self, from_, _identities, features, _data, _node):
         if nbxmpp.NS_BLOCKING not in features:
@@ -75,15 +61,6 @@ class Blocking:
 
         log.info('Discovered blocking: %s', from_)
 
-    @ensure_online
-    def get_blocking_list(self, callback=None):
-        log.info('Request list')
-        if callback is None:
-            callback = self._blocking_list_received
-
-        self._con.connection.get_module('Blocking').get_blocking_list(
-            callback=callback)
-
     def _blocking_list_received(self, result):
         if result.is_error:
             log.info('Error: %s', result.error)
@@ -98,7 +75,7 @@ class Blocking:
         childs = reply.getChildren()
         for child in childs:
             reply.delChild(child)
-        self._con.connection.send(reply)
+        self._nbxmpp().send(reply)
 
         changed_list = []
 
@@ -149,7 +126,7 @@ class Blocking:
         log.info('Presence probe: %s', jid)
         # Send a presence Probe to get the current Status
         probe = nbxmpp.Presence(jid, 'probe', frm=self._con.get_own_jid())
-        self._con.connection.send(probe)
+        self._nbxmpp().send(probe)
 
 
 class BlockingEvent(NetworkIncomingEvent):
diff --git a/gajim/common/modules/muc.py b/gajim/common/modules/muc.py
index 650c9993fc0765d0ca29bd22f94b8cf375f54018..1510ced2be33aaee57bf31bb514717c5bc7adc5c 100644
--- a/gajim/common/modules/muc.py
+++ b/gajim/common/modules/muc.py
@@ -30,14 +30,32 @@ from gajim.common.helpers import AdditionalDataDict
 from gajim.common.caps_cache import muc_caps_cache
 from gajim.common.nec import NetworkEvent
 from gajim.common.modules.bits_of_binary import store_bob_data
+from gajim.common.modules.base import BaseModule
+
 
 log = logging.getLogger('gajim.c.m.muc')
 
 
-class MUC:
+class MUC(BaseModule):
+
+    _nbxmpp_extends = 'MUC'
+    _nbxmpp_methods = [
+        'get_affiliation',
+        'set_role',
+        'set_affiliation',
+        'set_config',
+        'set_subject',
+        'cancel_config',
+        'send_captcha',
+        'decline',
+        'invite',
+        'request_config',
+        'request_voice',
+        'destroy',
+    ]
+
     def __init__(self, con):
-        self._con = con
-        self._account = con.name
+        BaseModule.__init__(self, con)
 
         self.handlers = [
             StanzaHandler(name='presence',
@@ -75,28 +93,7 @@ class MUC:
                           priority=49)
         ]
 
-        self._nbmxpp_methods = [
-            'get_affiliation',
-            'set_role',
-            'set_affiliation',
-            'set_config',
-            'set_subject',
-            'cancel_config',
-            'send_captcha',
-            'decline',
-            'request_voice',
-            'destroy',
-        ]
-
-    def __getattr__(self, key):
-        if key not in self._nbmxpp_methods:
-            raise AttributeError
-        if not app.account_is_connected(self._account):
-            log.warning('Account %s not connected, cant use %s',
-                        self._account, key)
-            return
-        module = self._con.connection.get_module('MUC')
-        return getattr(module, key)
+        self._register_callback('request_config', self._config_received)
 
     def pass_disco(self, from_, identities, features, _data, _node):
         for identity in identities:
@@ -423,24 +420,13 @@ class MUC:
             raise nbxmpp.NodeProcessed
 
     def invite(self, room, to, reason=None, continue_=False):
-        if not app.account_is_connected(self._account):
-            return
-
         type_ = InviteType.MEDIATED
         contact = app.contacts.get_contact_from_full_jid(self._account, to)
         if contact and contact.supports(nbxmpp.NS_CONFERENCE):
             type_ = InviteType.DIRECT
 
         password = app.gc_passwords.get(room, None)
-        self._con.connection.get_module('MUC').invite(
-            room, to, reason, password, continue_, type_)
-
-    def request_config(self, room_jid):
-        if not app.account_is_connected(self._account):
-            return
-
-        self._con.connection.get_module('MUC').request_config(
-            room_jid, callback=self._config_received)
+        self._nbxmpp('MUC').invite(room, to, reason, password, continue_, type_)
 
     def _config_received(self, result):
         if result.is_error: